GS2-SerialKey Transaction Actions
Combining actions, and concurrency
The background common to every service is collected in Combining Transaction Actions. Read that first; the rest of this section is what GS2-SerialKey adds to it.
The transaction actions of GS2-SerialKey address one serial key, identified by the combination of namespace and serial code. A key records whether it has been used and by whom, and it is rewritten as a whole on every update.
| Operation | Repeated in one transaction | Across a nested transaction | Boundary that separates targets |
|---|---|---|---|
Using a keyUseByUserId | Rejected when the transaction is issued | Fails | namespace, code |
Undoing a useRevertUseByUserId | Rejected when the transaction is issued | Fails | namespace, code |
Issuing a keyIssueOnce | Each one issues a new key; they never collide | No collision | every issue is its own target |
Verifying a codeVerifyCodeByUserId | First wins | No collision, because it only reads | verify type, user, code, campaign |
Using and undoing are separate rows sharing a boundary, so one key cannot be used and undone in the same transaction. Different keys are different targets, so using several of them is fine. Two undos against the same key placed in one transaction are rejected when the transaction is issued as well, because there is no reason to place two of them side by side and because the second one always fails once the first has put the key back to unused.
A serial key can be used once. That is the reason serial keys exist. A second use of the same key fails, whether from the same transaction or a later one. Two uses of the same key placed in one transaction are rejected when the transaction is issued. The second one cannot be combined into the first, so keep one per transaction. The user is not part of that boundary: a key records who used it, but it is reached by namespace and code alone, so two uses of one code written for two different users are still one target and are rejected in the same way. Buying a display item that uses a serial key with a quantity of 2 or more runs into this as well, because the actions are stacked once per unit. Releasing several skill tree nodes that ask for the same code runs into it too.
A key cannot be issued and used in the same transaction. Using works from the state at the start of the transaction, and at that point the key does not exist yet. Verify actions look at the same starting state, so a code used by the same transaction still reads as unused.
Using a key hands out whatever its campaign grants. Those are issued as a transaction of their own, so the restrictions of whichever services they belong to apply, and so does the section below. Using several keys in one transaction is fine as far as GS2-SerialKey is concerned, but everything they grant gathers into the same transaction: where two campaigns grant the same item or the same currency, read that service’s page.
Take care with nested transactions
The same serial key used from the inside and undone from the outside makes the transaction fail.
If you want to avoid these restrictions
Using a key is a consume action; undoing a use and issuing a key are acquire actions. acquireActionUseJobQueue separates the grants from each other only, never from the use itself. Turning enableAtomicCommit off clears a collision, but weigh that carefully: a serial key that has been used cannot be used again even if a grant fails afterwards.
Neither two uses of the same key nor two undos of one have such an escape. The check runs before either setting is applied, and a serial code is meant to be used once, so turning them on changes nothing here. The check looks only at the namespace and the code, so two uses of one campaign code, which may be used any number of times, are rejected in the same way. Split it into separate transactions, buy a display item that uses a serial key one at a time, and release skill tree nodes that ask for the same code separately.
Concurrency and retries
Using a key checks that it has not been used already, so using the same code at the same time makes the one confirmed later fail. This is what stops one code being used twice, and once the key is used a retry does not change it.
Different keys are different targets, so using different codes at the same time does not conflict inside GS2-SerialKey itself.
Verify Action
Gs2SerialKey:VerifyCodeByUserId
Verify the validity of the Serial Code by User ID
Verifies a serial code for the specified user without consuming it. Supports verification against a specific campaign model name, and can check whether the code is active (unused) or inactive (already used).
Quantity specification supported: NO
| Type | Condition | Required | Default | Value Limits | Description | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||||||||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||||||||
| code | string | ✓ | ~ 48 chars | Serial Code The serial code string in the format “XXXXX-XXXX-XXXXX-XXXX-XXXX”. Each code is unique and includes campaign identification information. The code format and data length are fixed and cannot be changed. | ||||||||
| campaignModelName | string | ~ 128 chars | Campaign name The name of the campaign model this serial code belongs to. Campaign information is embedded within the serial code itself, so only the Namespace needs to be specified when using the code. | |||||||||
| verifyType | string (enum) enum { “active”, “inactive” } | ✓ | Verification type
| |||||||||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2SerialKey:VerifyCodeByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"code": "[string]Serial Code",
"campaignModelName": "[string]Campaign name",
"verifyType": "[string]Verification type",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2SerialKey:VerifyCodeByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
code: "[string]Serial Code"
campaignModelName: "[string]Campaign name"
verifyType: "[string]Verification type"
timeOffsetToken: "[string]Time offset token"transaction.service("serialKey").verify.verify_code_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
code="[string]Serial Code",
campaignModelName="[string]Campaign name",
verifyType="[string]Verification type",
timeOffsetToken="[string]Time offset token",
})Consume Action
Gs2SerialKey:UseByUserId
Use Serial Code by User ID
Marks a serial code as used by the specified user. Validates that the code exists and has not already been used.
Quantity specification supported: NO
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| code | string | ✓ | ~ 48 chars | Serial Code The serial code string in the format “XXXXX-XXXX-XXXXX-XXXX-XXXX”. Each code is unique and includes campaign identification information. The code format and data length are fixed and cannot be changed. | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2SerialKey:UseByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"code": "[string]Serial Code",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2SerialKey:UseByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
code: "[string]Serial Code"
timeOffsetToken: "[string]Time offset token"transaction.service("serialKey").consume.use_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
code="[string]Serial Code",
timeOffsetToken="[string]Time offset token",
})Acquire Action
Gs2SerialKey:RevertUseByUserId
Set Serial Code to Unused by User ID
Reverts a used serial code back to unused status. Validates that the code is currently in used state before reverting.
Quantity specification supported: NO
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| code | string | ✓ | ~ 48 chars | Serial Code The serial code string in the format “XXXXX-XXXX-XXXXX-XXXX-XXXX”. Each code is unique and includes campaign identification information. The code format and data length are fixed and cannot be changed. | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2SerialKey:RevertUseByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"code": "[string]Serial Code",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2SerialKey:RevertUseByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
code: "[string]Serial Code"
timeOffsetToken: "[string]Time offset token"transaction.service("serialKey").acquire.revert_use_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
code="[string]Serial Code",
timeOffsetToken="[string]Time offset token",
})Gs2SerialKey:IssueOnce
Issue a serial code
Issues a single serial code immediately. The code is generated using AES encryption and base32 encoding in the format XXXXX-XXXX-XXXXX-XXXX-XXXXX. Unlike batch issuance jobs, this generates and returns a code synchronously.
Quantity specification supported: NO
Reversible action: NO
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| campaignModelName | string | ✓ | ~ 128 chars | Campaign Model name | ||
| metadata | string | ~ 2048 chars | Metadata Arbitrary values can be set in the metadata. Since they do not affect GS2’s behavior, they can be used to store information used in the game. |
{
"action": "Gs2SerialKey:IssueOnce",
"request": {
"namespaceName": "[string]Namespace name",
"campaignModelName": "[string]Campaign Model name",
"metadata": "[string]Metadata"
}
}action: Gs2SerialKey:IssueOnce
request:
namespaceName: "[string]Namespace name"
campaignModelName: "[string]Campaign Model name"
metadata: "[string]Metadata"transaction.service("serialKey").acquire.issue_once({
namespaceName="[string]Namespace name",
campaignModelName="[string]Campaign Model name",
metadata="[string]Metadata",
})