Documentation index for AI agents

GS2-Money2 트랜잭션 액션

검증/소비/입수 각 트랜잭션 액션의 사양

액션의 조합과 동시 실행

모든 서비스에 공통되는 전제는 트랜잭션 액션의 조합에 정리되어 있습니다. 먼저 그쪽을 읽어 주십시오. 이 절의 나머지는 GS2-Money2 고유의 내용입니다.

GS2-Money2의 트랜잭션 액션은 네임스페이스·사용자·슬롯의 조합으로 결정되는 하나의 월렛을 대상으로 합니다. 월렛은 유상 잔액과 무상 잔액을 함께 보관합니다.

무상 잔액만 움직이는 업데이트는 증분으로 기록되므로 합산됩니다. 유상 잔액이 움직이는 업데이트는 월렛 전체를 다시 씁니다.

조작같은 행을 겹쳤을 때중첩 너머다른 대상이 되는 경계
무상 통화만 움직이는 입금·출금
DepositByUserId WithdrawByUserId
혼재해도 된다. 통합되어 금액이 합산된다합산된다네임스페이스·사용자·슬롯
유상 통화가 움직이는 입금·출금
DepositByUserId WithdrawByUserId
입금은 입금 트랜잭션이 하나의 리스트로 묶이고, 출금은 금액이 합산된다. 입금과 출금의 공존은 실패한다실패한다네임스페이스·사용자·슬롯
영수증의 검증
VerifyReceiptByUserId
실패한다같은 거래 ID로 해석되면 실패한다네임스페이스·사용자(행 자체는 거래 ID 단위)

무상 통화의 업데이트는 증분이므로, 무상의 입금과 출금을 하나의 트랜잭션에 넣어도 양쪽이 적용됩니다. 유상 잔액이 움직이는 경우에는 월렛 전체가 다시 쓰이므로 두 개의 행을 함께 둘 수 없습니다.

무상만으로는 부족한 출금은 유상 잔액을 파고들므로, 유상을 쓸 의도가 없어도 두 번째 행의 취급이 됩니다.

다음의 경우에는 월렛 전체를 다시 쓰는 경로로 떨어져 두 번째 행과 같은 동작이 됩니다. 유상 로트가 움직이는 경우, 월렛이 예전 저장 형식으로 보관되어 있는 경우, 무상 로트가 정확히 1개가 아닌 경우, summary가 기록된 로트와 맞지 않는 경우입니다. 뒤의 두 가지는 임포트로 반입된 데이터에서만 일어납니다.

sharedFreeCurrency를 활성화한 경우, 무상 잔액은 모든 슬롯 분이 슬롯 0의 한 행에 보관됩니다. 슬롯 0에 대한 조작은 증분 경로를 타지만, 그 외의 슬롯에 대해 발행된 무상 조작은 전체를 다시 쓰는 형태가 됩니다.

잔액은 통합 후의 합계에 대해 판정됩니다. 단독으로는 충분한 출금이라도 다른 출금과 합쳐지면 거부될 수 있습니다.

유상 잔액에서만 빼는 출금과 일반 출금은 같은 슬롯에 나열할 수 없습니다. 어느 잔액에서 뺄지가 어긋난 채 합산하면 의도하지 않은 잔액에서 빠지므로, paidOnly가 다른 출금을 같은 월렛에 지정하면 발행 시에 오류가 됩니다. 나누어 지정해 주십시오.

VerifyReceiptByUserId는 이름은 검증이지만 종별로는 소비 액션입니다. 월렛은 건드리지 않지만, 영수증을 기록하는 전용 행은 만듭니다. 소비 액션 목록에 두어 주십시오. 트랜잭션 시작 시점의 상태를 보므로, 같은 트랜잭션에서 기록된 영수증을 검증할 수는 없습니다.

같은 사용자에 대한 영수증 검증끼리는 영수증이 다르더라도 발행 시점에 오류가 됩니다. 영수증은 거래 ID를 거쳐 행에 도달하는데, 거래 ID를 추출하는 방법이 스토어마다 다릅니다. Google Play에서는 보낸 transactionId를 한 번도 보지 않고, 페이로드 안쪽의 orderId가 행을 결정합니다. 즉 겉보기에 다른 영수증이 같은 과금을 가리키는 경우도, 겉보기에 같은 영수증이 다른 과금인 경우도 있습니다. 추측으로 합치지 않고, 하나의 트랜잭션에 1건만 놓아 주십시오. 영수증을 검증하는 진열 상품을 수량 2 이상으로 구입한 경우에도 액션이 개수만큼 쌓이므로 여기에 해당합니다.

이 검증을 입금과 같은 트랜잭션에 두면, 영수증이 정당함을 확인한 뒤에 입금한다는 구성이 가능합니다. 이중 계상을 방지하려면 이 형태로 해 주십시오.

중첩된 트랜잭션에 주의

무상 통화의 업데이트는 중첩 너머에서도 안전합니다. 어느 경로로 도착하든 한 번의 업데이트로 묶입니다.

유상 잔액이 움직이는 경우에는 월렛 전체가 다시 쓰이므로, 안쪽과 바깥쪽 양쪽에서 건드리면 트랜잭션이 실패합니다. 통화는 비용으로 사용되는 경우가 많으므로 이 상황은 일어나기 쉽습니다. 바깥쪽에서 월렛으로 지불하는 구매와, 안쪽에서도 월렛으로 지불하는 경품이 공존하면 통과하지 않습니다.

같은 금액의 입금이 안쪽과 바깥쪽에서 도착한 경우에는 실패하지 않고 1건으로 묶입니다. 100의 입금이 두 개 있어도 들어가는 것은 100뿐이며 오류는 발생하지 않습니다. 이것은 월렛 전체를 다시 쓰는 두 번째 행에서의 이야기이며, 금액이 다르면 실패하므로 조용히 맞지 않는 것은 금액이 일치했을 때뿐입니다.

제한을 회피하고 싶은 경우

월렛에의 입금은 획득 액션, 출금과 영수증의 검증은 소비 액션입니다. 입금끼리의 충돌은 acquireActionUseJobQueue를 활성화하면 해소되지만, 입금과 출금의 공존은 enableAtomicCommit을 비활성화하지 않으면 해소되지 않습니다. 통화를 다루는 이상, 이 판단은 신중하게 해 주십시오.

영수증의 검증에는 우회 수단이 없습니다. 검사는 어느 설정보다도 먼저 실행되며, 영수증은 1건의 과금이라 같은 과금을 이중으로 검증해서는 안 되므로, 활성화해도 달라지지 않습니다. 트랜잭션을 나누고, 영수증을 검증하는 진열 상품은 1개씩 구입해 주십시오.

동시 실행과 재시도

무상 통화의 업데이트는 결과가 범위에 들어가는 한, 동시 실행 요청이 몇 개 겹쳐도 충돌하지 않습니다. 증분으로 기록하는 이점이 여기에 나타납니다. 퀘스트 보상·로그인 보너스·미션 보상처럼 무상 통화를 배분하는 것끼리가 서로 충돌하지 않게 됩니다.

유상 잔액이 움직이는 경우에는 리비전 대조를 수반하여 기록되므로, 나중에 확정된 쪽이 충돌(409)이 됩니다. 요청 내용에 문제가 있는 것은 아니므로, 재시도하면 성공합니다. 재시도 시에는 최신 잔액으로 다시 판정됩니다.

슬롯이 다르면 다른 월렛이므로, 같은 사용자라도 슬롯이 다르면 충돌하지 않습니다. 예외는 sharedFreeCurrency로, 이 경우에는 모든 슬롯의 무상 잔액이 한 행에 실립니다.


Consume Action

소비 액션

Gs2Money2:WithdrawByUserId

사용자 ID를 지정하여 지갑에서 잔액 소비

지정된 사용자의 지갑에서 지정된 양의 통화를 소비합니다.
paidOnly가 false인 경우 무상 통화가 먼저 소비되고, 그다음 유상 통화가 소비됩니다.

수량 지정 가능한 액션: 예

반전 가능한 액션: 예

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
userIdstring
~ 128자사용자ID
#{userId}로 설정하면 로그인 중인 사용자ID로 치환됩니다.
slotint
0 ~ 100000000슬롯 번호
지갑 슬롯을 식별합니다. 플랫폼 간 잔액 공유가 허용되지 않는 경우, 서로 다른 슬롯을 사용하여 플랫폼별로 통화를 나누어 관리할 수 있습니다(예: iOS용과 Android용).
withdrawCountint
1 ~ 2147483646소비할 유료 통화 수량
paidOnlyboolfalse유료 통화만을 대상으로 할지 여부
timeOffsetTokenstring~ 1024자타임 오프셋 토큰
{
    "action": "Gs2Money2:WithdrawByUserId",
    "request": {
        "namespaceName": "[string]네임스페이스 이름",
        "userId": "[string]사용자ID",
        "slot": "[int]슬롯 번호",
        "withdrawCount": "[int]소비할 유료 통화 수량",
        "paidOnly": "[bool]유료 통화만을 대상으로 할지 여부",
        "timeOffsetToken": "[string]타임 오프셋 토큰"
    }
}
action: Gs2Money2:WithdrawByUserId
request:
  namespaceName: "[string]네임스페이스 이름"
  userId: "[string]사용자ID"
  slot: "[int]슬롯 번호"
  withdrawCount: "[int]소비할 유료 통화 수량"
  paidOnly: "[bool]유료 통화만을 대상으로 할지 여부"
  timeOffsetToken: "[string]타임 오프셋 토큰"
transaction.service("money2").consume.withdraw_by_user_id({
    namespaceName="[string]네임스페이스 이름",
    userId="[string]사용자ID",
    slot="[int]슬롯 번호",
    withdrawCount="[int]소비할 유료 통화 수량",
    paidOnly="[bool]유료 통화만을 대상으로 할지 여부",
    timeOffsetToken="[string]타임 오프셋 토큰",
})

Gs2Money2:VerifyReceiptByUserId

사용자 ID를 지정하여 영수증을 사용 완료 처리

지정된 사용자의 구매 영수증을 검증하고, 사용 완료로 기록합니다.
이벤트 로그가 기록되며, 네임스페이스에 설정된 영수증 검증 스크립트가 실행됩니다.

수량 지정 가능한 액션: 아니오

반전 가능한 액션: 아니오

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
userIdstring
~ 128자사용자ID
#{userId}로 설정하면 로그인 중인 사용자ID로 치환됩니다.
contentNamestring
~ 128자스토어 콘텐츠 모델 이름
receiptReceipt
영수증
timeOffsetTokenstring~ 1024자타임 오프셋 토큰
{
    "action": "Gs2Money2:VerifyReceiptByUserId",
    "request": {
        "namespaceName": "[string]네임스페이스 이름",
        "userId": "[string]사용자ID",
        "contentName": "[string]스토어 콘텐츠 모델 이름",
        "receipt": {
            "Store": "[string]스토어",
            "TransactionID": "[string]트랜잭션 ID",
            "Payload": "[string]페이로드"
        },
        "timeOffsetToken": "[string]타임 오프셋 토큰"
    }
}
action: Gs2Money2:VerifyReceiptByUserId
request:
  namespaceName: "[string]네임스페이스 이름"
  userId: "[string]사용자ID"
  contentName: "[string]스토어 콘텐츠 모델 이름"
  receipt: 
    Store: "[string]스토어"
    TransactionID: "[string]트랜잭션 ID"
    Payload: "[string]페이로드"
  timeOffsetToken: "[string]타임 오프셋 토큰"
transaction.service("money2").consume.verify_receipt_by_user_id({
    namespaceName="[string]네임스페이스 이름",
    userId="[string]사용자ID",
    contentName="[string]스토어 콘텐츠 모델 이름",
    receipt={
        store="[string]스토어",
        transactionID="[string]트랜잭션 ID",
        payload="[string]페이로드"
    },
    timeOffsetToken="[string]타임 오프셋 토큰",
})

Acquire Action

입수 액션

Gs2Money2:DepositByUserId

사용자 ID를 지정하여 지갑 잔액에 가산

지정된 사용자의 지갑에 지정된 입금 트랜잭션을 추가합니다.
각 입금 트랜잭션에 대해 이벤트 로그가 기록됩니다.

수량 지정 가능한 액션: 예

반전 가능한 액션: 예

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
userIdstring
~ 128자사용자ID
#{userId}로 설정하면 로그인 중인 사용자ID로 치환됩니다.
slotint
0 ~ 100000000슬롯 번호
지갑 슬롯을 식별합니다. 플랫폼 간 잔액 공유가 허용되지 않는 경우, 서로 다른 슬롯을 사용하여 플랫폼별로 통화를 나누어 관리할 수 있습니다(예: iOS용과 Android용).
depositTransactionsList<DepositTransaction>
1 ~ 1000 items입금 트랜잭션 목록
timeOffsetTokenstring~ 1024자타임 오프셋 토큰
{
    "action": "Gs2Money2:DepositByUserId",
    "request": {
        "namespaceName": "[string]네임스페이스 이름",
        "userId": "[string]사용자ID",
        "slot": "[int]슬롯 번호",
        "depositTransactions": [
            {
                "price": "[double]구매 가격",
                "currency": "[string]통화 코드",
                "count": "[int]과금 통화 수량",
                "depositedAt": "[long]입금 일시"
            }
        ],
        "timeOffsetToken": "[string]타임 오프셋 토큰"
    }
}
action: Gs2Money2:DepositByUserId
request:
  namespaceName: "[string]네임스페이스 이름"
  userId: "[string]사용자ID"
  slot: "[int]슬롯 번호"
  depositTransactions: 
    - price: "[double]구매 가격"
      currency: "[string]통화 코드"
      count: "[int]과금 통화 수량"
      depositedAt: "[long]입금 일시"
  timeOffsetToken: "[string]타임 오프셋 토큰"
transaction.service("money2").acquire.deposit_by_user_id({
    namespaceName="[string]네임스페이스 이름",
    userId="[string]사용자ID",
    slot="[int]슬롯 번호",
    depositTransactions={
        {
            price="[double]구매 가격",
            currency="[string]통화 코드",
            count="[int]과금 통화 수량",
            depositedAt="[long]입금 일시"
        }
    },
    timeOffsetToken="[string]타임 오프셋 토큰",
})