Skip to main content

Payment API

Payment API

TudadaSDK.Instance.payment.* is the payment API for purchasing in-game products. The game requests purchases by productId only, without needing to know the payment method or rail. Payment method selection, pricing, receipt verification, and granting are all handled by the Tudada platform.

All four methods are callback-based — they take onSuccess (a result-specific type) and onFail (Action<PaymentFailResult>). Always branch on failure using PaymentFailResult.code.

Payment availability · store visibility
  • Payment-unsupported environmentGetProducts's onFail is called with PAYMENT_UNAVAILABLE.
  • Supported environment, no matching productsproducts is an empty array (a normal response). When it's empty, do not show the store UI.
  • Communication failure — fails with NETWORK_ERROR.
Grant confirmation belongs on the game server

The result of Purchase is reduced information for display/UX. Confirm the final grant on your game server. Transactions do not include amounts or receipts; authoritative grant information is held by the game server and the Tudada server.

Note: Payment works only in WebGL builds. In the Unity Editor simulation, onFail is called with PAYMENT_UNAVAILABLE (see Editor Simulation).

payment.GetProducts(onSuccess, onFail)

Retrieves the list of purchasable products. If there are no products available for sale, products is an empty array.

Parameters:

ParameterTypeRequiredDescription
onSuccessAction<GetProductsResult>-Success callback
onFailAction<PaymentFailResult>-Failure callback

Success Response (GetProductsResult):

FieldTypeDescription
productsPaymentProduct[]List of purchasable products (empty array if none)
errMsgstringResult message
TudadaSDK.Instance.payment.GetProducts(
onSuccess: (res) => {
if (res.products.Length == 0) { HideStore(); return; } // empty array → hide store
foreach (var p in res.products)
Debug.Log($"{p.productId} / {p.name} / {p.price} {p.currency}");
RenderStore(res.products);
},
onFail: (err) => Debug.LogError("Product query failed: " + err.code)
);

payment.GetProduct(productId, onSuccess, onFail)

Retrieves a single product. If it does not exist or is not for sale, onFail is called with PRODUCT_NOT_FOUND.

Parameters:

ParameterTypeRequiredDescription
productIdstringID of the product to query
onSuccessAction<GetProductResult>-Success callback
onFailAction<PaymentFailResult>-Failure callback

Success Response (GetProductResult):

FieldTypeDescription
productPaymentProductProduct information
errMsgstringResult message
TudadaSDK.Instance.payment.GetProduct("coin_100",
onSuccess: (res) => Debug.Log($"{res.product.name} / {res.product.price}"),
onFail: (err) => {
if (err.code == PaymentErrorCode.ProductNotFound) ShowToast("This product is not for sale.");
}
);

payment.Purchase(productId, passthroughPayload, onSuccess, onFail)

Purchases a product. On success, the transaction is delivered in the GRANTED (granted) or PENDING (accepted, awaiting confirmation) state; otherwise onFail is called.

Parameters:

ParameterTypeRequiredDescription
productIdstringID of the product to purchase
passthroughPayloadstring-Opaque echo string (≤1000 UTF-8 bytes). Returned verbatim during purchase verification. No secrets or personal data
onSuccessAction<PurchaseResult>-Success callback
onFailAction<PaymentFailResult>-Failure callback

Success Response (PurchaseResult):

FieldTypeDescription
transactionPaymentTransactionTransaction (GRANTED / PENDING)
errMsgstringResult message
TudadaSDK.Instance.payment.Purchase("coin_100", "order-1234",
onSuccess: (res) => {
var txn = res.transaction;
if (txn.status == PaymentTxnStatus.Granted) {
// Granted — reflect in UX (confirm the final grant on the game server)
} else if (txn.status == PaymentTxnStatus.Pending) {
// Accepted — awaiting confirmation. You can check the later status with GetTransaction
}
},
onFail: (err) => {
if (err.code == PaymentErrorCode.UserCancelled) return; // user closed the payment window
Debug.LogError("Payment failed: " + err.code);
}
);

payment.GetTransaction(txnKey, onSuccess, onFail)

Retrieves a transaction's status and result. Use it, for example, to follow up when Purchase ended in PENDING. If it does not exist or is not owned, onFail is called with TRANSACTION_NOT_FOUND.

Parameters:

ParameterTypeRequiredDescription
txnKeystringKey of the transaction to query
onSuccessAction<GetTransactionResult>-Success callback
onFailAction<PaymentFailResult>-Failure callback

Success Response (GetTransactionResult):

FieldTypeDescription
transactionPaymentTransactionTransaction (all 5 statuses possible)
errMsgstringResult message
TudadaSDK.Instance.payment.GetTransaction(prevTxnKey,
onSuccess: (res) => Debug.Log("Status: " + res.transaction.status),
onFail: (err) => Debug.LogError("Query failed: " + err.code)
);

Data Types

PaymentProduct

FieldTypeDescription
productIdstringMarket-agnostic single product ID (specified when purchasing)
namestringRegistered product name
pricestringCurrent price (with discount applied) — a display-ready string (e.g. "4,400", "4.99"). No arithmetic; display as-is
currencystringCurrency identifier string (e.g. "KRW"). Display unrecognized values as the raw string
originalPricestringPre-discount price (display-ready string). Empty string if there is no discount. For strikethrough display, etc.
discountRatestringDiscount rate display string (e.g. "20%"). A marketing label — do not back-calculate from the price

All prices are display-ready strings. Thousands separators and decimal places are already applied, so output them as-is and do not use them in calculations.

PaymentTransaction

A reduced view for verification/UX (no amount or receipt).

FieldTypeDescription
txnKeystringUnique key — used for re-querying
statusstringTransaction status (table below, PaymentTxnStatus constants)
productIdstringTransaction product
failReasonstringDetailed reason for diagnostics/logging (do not expose to users, value may change). Empty string if absent
passthroughPayloadstringThe value passed at purchase, verbatim. Empty string if omitted or empty
requestedAtlongRequest time (Unix epoch ms)
grantedAtlongFirst grant time (epoch ms). 0 if not granted (check existence with hasGrantedAt)

Transaction status (status):

ValueDescription
PENDINGAccepted, grant not yet confirmed
GRANTEDGranted
FAILEDFailed
PARTIALLY_REFUNDEDPartially refunded (a prior grant exists)
REFUNDEDRefunded (a prior grant exists)

Purchase delivers only GRANTED / PENDING, while GetTransaction can return all 5 statuses.

Error Handling

Failures are delivered via PaymentFailResult in onFail, and you always branch on code.

FieldTypeDescription
codestringError code (table below) — the branching criterion. PaymentErrorCode constants
errMsgstringMessage (do not expose to users)
txnKeystringPresent only on failures where a transaction was recorded. Empty string if absent
failReasonstringDetailed reason for diagnostics/logging. Empty string if absent

Error codes (code, PaymentErrorCode constants):

CodeDescription
PAYMENT_UNAVAILABLEPayment-unavailable environment
USER_CANCELLEDUser closed the payment window
PAYMENT_FAILEDPayment failed (card declined, insufficient balance, etc.)
PRODUCT_NOT_FOUNDUnregistered productId
INVALID_PARAMInvalid argument (passthroughPayload over limit, etc.)
PURCHASE_REJECTEDVerification/grant rejected
PAYMENT_IN_PROGRESSA new call is rejected because a purchase is in progress (one at a time)
TRANSACTION_NOT_FOUNDTransaction does not exist or is not owned
SESSION_EXPIREDSession expired — re-login required
NETWORK_ERRORCommunication failure
TudadaSDK.Instance.payment.Purchase("coin_100", null,
onSuccess: (res) => { /* ... */ },
onFail: (err) => {
switch (err.code) {
case PaymentErrorCode.UserCancelled: break; // silently ignore
case PaymentErrorCode.PaymentInProgress: ShowToast("Processing..."); break;
case PaymentErrorCode.SessionExpired: RequestRelogin(); break;
default: ShowToast("Payment failed.");
}
}
);