Documentation index for AI agents

GS2-SerialKey Transaction Actions

Specification of verify/consume/acquire 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.

OperationRepeated in one transactionAcross a nested transactionBoundary that separates targets
Using a key
UseByUserId
Rejected when the transaction is issuedFailsnamespace, code
Undoing a use
RevertUseByUserId
Rejected when the transaction is issuedFailsnamespace, code
Issuing a key
IssueOnce
Each one issues a new key; they never collideNo collisionevery issue is its own target
Verifying a code
VerifyCodeByUserId
First winsNo collision, because it only readsverify 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

TypeConditionRequiredDefaultValue LimitsDescription
namespaceNamestring
~ 128 charsNamespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
userIdstring
~ 128 charsUser ID
Specify #{userId} to substitute the currently logged-in user’s ID.
codestring
~ 48 charsSerial 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.
campaignModelNamestring~ 128 charsCampaign 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.
verifyTypestring (enum)
enum {
  “active”,
  “inactive”
}
Verification type
DefinitionDescription
activeActive
inactiveInactive
timeOffsetTokenstring~ 1024 charsTime 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

TypeConditionRequiredDefaultValue LimitsDescription
namespaceNamestring
~ 128 charsNamespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
userIdstring
~ 128 charsUser ID
Specify #{userId} to substitute the currently logged-in user’s ID.
codestring
~ 48 charsSerial 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.
timeOffsetTokenstring~ 1024 charsTime 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

TypeConditionRequiredDefaultValue LimitsDescription
namespaceNamestring
~ 128 charsNamespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
userIdstring
~ 128 charsUser ID
Specify #{userId} to substitute the currently logged-in user’s ID.
codestring
~ 48 charsSerial 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.
timeOffsetTokenstring~ 1024 charsTime 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

TypeConditionRequiredDefaultValue LimitsDescription
namespaceNamestring
~ 128 charsNamespace name
Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
campaignModelNamestring
~ 128 charsCampaign Model name
metadatastring~ 2048 charsMetadata
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",
})