# Get Account Source: https://docs.toffeepay.com/api-reference/accounts/get GET /v1/accounts/{id} Returns account details by its id # List Accounts Source: https://docs.toffeepay.com/api-reference/accounts/list GET /v1/accounts List accounts # Create Bonus Source: https://docs.toffeepay.com/api-reference/bonuses/create Post /v1alpha1/bonuses Add bonus to user's wallet account # Get Bonus Source: https://docs.toffeepay.com/api-reference/bonuses/get GET /v1alpha1/bonuses/{id_or_key} Get bonus info by id or external key # List Bonuses Source: https://docs.toffeepay.com/api-reference/bonuses/list GET /v1alpha1/bonuses List bonuses filtered by provided params # Get Deposit Source: https://docs.toffeepay.com/api-reference/deposits/get GET /v1/deposits/{id} Returns deposit details by its id # List Deposits Source: https://docs.toffeepay.com/api-reference/deposits/list GET /v1/deposits List deposits # List Disputes Source: https://docs.toffeepay.com/api-reference/disputes/list GET /v1/disputes # Create Offer Source: https://docs.toffeepay.com/api-reference/offers/create POST /v1alpha1/offers Create hub offer # Delete Offer Source: https://docs.toffeepay.com/api-reference/offers/delete DELETE /v1alpha1/offers/{id} Delete hub offer # Get Offer Source: https://docs.toffeepay.com/api-reference/offers/get GET /v1alpha1/offers/{id} Get hub offer by its id # List Offers Source: https://docs.toffeepay.com/api-reference/offers/list GET /v1alpha1/offers List hub offers # Update Offer Source: https://docs.toffeepay.com/api-reference/offers/update PATCH /v1alpha1/offers/{id} Update hub offer # Cancel Payment Source: https://docs.toffeepay.com/api-reference/payments/cancel POST /v1/payments/{id}/cancel Initiates a payment cancellation of the authorization # Complete Payment Source: https://docs.toffeepay.com/api-reference/payments/complete POST /v1/payments/{id}/complete Captures a payment for the session after authorization # Get Payment Source: https://docs.toffeepay.com/api-reference/payments/get GET /v1/payments/{id} Returns payment details by its id # List Payments Source: https://docs.toffeepay.com/api-reference/payments/list GET /v1/payments List completed payments # Create Refund Source: https://docs.toffeepay.com/api-reference/refunds/create POST /v1/refunds # Get Refund Source: https://docs.toffeepay.com/api-reference/refunds/get GET /v1/refunds/{id} # List Refunds Source: https://docs.toffeepay.com/api-reference/refunds/list GET /v1/refunds # Cancel Session Source: https://docs.toffeepay.com/api-reference/sessions/cancel POST /v1/sessions/{id}/cancel Cancel an existing payment session # Create Session Source: https://docs.toffeepay.com/api-reference/sessions/create POST /v1/sessions Initiate a new payment session # Get Session Source: https://docs.toffeepay.com/api-reference/sessions/get GET /v1/sessions/{id} Returns session by its id # List Sessions Source: https://docs.toffeepay.com/api-reference/sessions/list GET /v1/sessions List payments sessions # Get Session Status Source: https://docs.toffeepay.com/api-reference/sessions/status GET /v1/sessions/{id}/status Returns session status by its id # deposit.failed Source: https://docs.toffeepay.com/api-reference/webhooks/deposit.failed webhook deposit.failed Fired when a deposit fails. # deposit.succeeded Source: https://docs.toffeepay.com/api-reference/webhooks/deposit.succeeded webhook deposit.succeeded Fired when a deposit is completed successfully. # dispute.created Source: https://docs.toffeepay.com/api-reference/webhooks/dispute.created webhook dispute.created Fired when a dispute is created and registered. # payment.authorized Source: https://docs.toffeepay.com/api-reference/webhooks/payment.authorized webhook payment.authorized Fired when a payment is authorized (e.g. for later capture). # payment.cancelled Source: https://docs.toffeepay.com/api-reference/webhooks/payment.cancelled webhook payment.cancelled Fired when a payment is cancelled. # payment.created Source: https://docs.toffeepay.com/api-reference/webhooks/payment.created webhook payment.created Fired when a payment is created. # payment.failed Source: https://docs.toffeepay.com/api-reference/webhooks/payment.failed webhook payment.failed Fired when a payment attempt fails. # payment.succeeded Source: https://docs.toffeepay.com/api-reference/webhooks/payment.succeeded webhook payment.succeeded Fired when a payment is successfully processed. # refund.created Source: https://docs.toffeepay.com/api-reference/webhooks/refund.created webhook refund.created Fired when a refund is created and processing begins. # refund.failed Source: https://docs.toffeepay.com/api-reference/webhooks/refund.failed webhook refund.failed Fired when a refund attempt fails. # refund.succeeded Source: https://docs.toffeepay.com/api-reference/webhooks/refund.succeeded webhook refund.succeeded Fired when a refund has been processed successfully. # session.cancelled Source: https://docs.toffeepay.com/api-reference/webhooks/session.cancelled webhook session.cancelled Fired when a session is cancelled by user. # session.created Source: https://docs.toffeepay.com/api-reference/webhooks/session.created webhook session.created Fired when a payment session is created. # session.expired Source: https://docs.toffeepay.com/api-reference/webhooks/session.expired webhook session.expired Fired when a session expires without payment. # session.failed Source: https://docs.toffeepay.com/api-reference/webhooks/session.failed webhook session.failed Fired when a payment attempt related to a session fails. # session.paid Source: https://docs.toffeepay.com/api-reference/webhooks/session.paid webhook session.paid Fired when a session is successfully paid. # Authentication Source: https://docs.toffeepay.com/authentication Access token authentication All requests to the ToffeePay API must be authenticated using a Bearer token in the `Authorization` header: ```http theme={null} Authorization: Bearer ``` You'll receive your access token when you register your game. Keep it secret and **only use it server-side**. ## Environments | Environment | Base URL | | ----------- | ----------------------------------- | | Production | `https://api.toffeepay.com` | | Sandbox | `https://api.sandbox.toffeepay.com` | Each environment uses separate access tokens. Use sandbox tokens for development and testing. ## Best Practices 1. **Server-side only**: Never expose access tokens in client-side code 2. **Use HTTPS**: All API requests must be made over HTTPS 3. **Store securely**: Use environment variables or a secrets manager to store access tokens ## Error Handling If authentication fails, the API returns a `401 Unauthorized` response. Common causes: * Missing or malformed `Authorization` header * Invalid or expired access token * Using a sandbox token against production (or vice versa) # Disputes Source: https://docs.toffeepay.com/disputes Track and manage payment disputes ## Dispute Flow ```mermaid theme={null} sequenceDiagram participant User as Cardholder / Bank participant API as ToffeePay participant Server as Game Server User->>API: Contests charge (Chargeback) API->>API: Log dispute petition API->>API: Recalculate user fraud persona (BAD_ACTOR / HIGH_RISK) API->>Server: Send dispute webhook (signed) Server->>Server: Verify webhook signature Server->>Server: Lock bad actor / restrict account ``` *** ## List Disputes Retrieve a list of disputes processed for your game. Send a server-side request to [list disputes](/api-reference/disputes/list): ```typescript theme={null} const { disputes } = await toffee.disputes.list({ paymentId: "pay_xyz789", limit: 50, }); ``` ```python theme={null} resp = await toffee.disputes.list(ListDisputesRequest( payment_id="pay_xyz789", limit=50, )) disputes = resp.disputes ``` ```go theme={null} paymentId := "pay_xyz789" limit := int32(50) resp, err := toffee.Disputes.List(ctx, &sdk.ListDisputesRequest{ PaymentId: &paymentId, Limit: &limit, }) disputes := resp.GetDisputes() ``` ```http theme={null} GET /v1/disputes?payment_id=pay_xyz789&limit=50&offset=0 Authorization: Bearer ``` **Query Parameters:** * `payment_id`: *Optional.* Filter disputes for a specific payment transaction ID. * `limit`: *Optional.* Max number of disputes to return (default: `100`, max: `250`). * `offset`: *Optional.* Number of records to skip (default: `0`). * `from`: *Optional.* Filter disputes created at or after this RFC 3339 timestamp. * `to`: *Optional.* Filter disputes created at or before this RFC 3339 timestamp. **Response:** An array of disputes. A dispute's resolution state is assigned and updated by the upstream payment provider, not by ToffeePay. Because that lifecycle is outside our control, a normalized `status` field is intentionally **not** exposed on the public API. It may be added later as a normalized value if there is demand. ```json theme={null} [ { "id": "dp_01kw1w89abcdefghij", "payment_id": "pay_9876543210", "reason": "FRAUDULENT - Cardholder claims transaction was not authorized.", "amount": 1500, "currency": "USD", "created_at": "2026-06-26T11:55:36Z" } ] ``` *** ## Dispute Properties Each dispute contains the following key properties: * `id`: The unique identifier for the dispute (e.g. `dp_01kw1w89abcdefghij`). * `payment_id`: *Optional.* The ID of the original payment transaction that was disputed. May be absent when the dispute could not be matched to a payment in our system. * `reason`: The raw explanation or reasoning code provided by the bank or payment processor. * `amount`: The total disputed transaction amount in minor currency units (e.g., `1500` = `$15.00`). * `currency`: The currency of the disputed transaction (`USD`, `GBP`, `EUR`). * `created_at`: RFC 3339 timestamp when the dispute was registered by ToffeePay. *** ## Webhook Events ToffeePay sends webhooks for the following dispute-related events: * [`dispute.created`](/api-reference/webhooks/dispute.created): When a dispute is created and registered See the [Webhooks](/webhooks) page for implementation details and signature verification. *** ## Handle Dispute Notifications When a dispute is processed, ToffeePay sends a **signed webhook** to your backend. **Sample Payload:** ```json theme={null} { "type": "dispute.created", "timestamp": "2026-06-29T12:00:00Z", "data": { "id": "dp_01kw1w89abcdefghij", "payment_id": "pay_9876543210", "reason": "FRAUDULENT - Cardholder claims transaction was not authorized.", "amount": 1500, "currency": "USD", "created_at": "2026-06-26T11:55:36Z" } } ``` For webhook signature verification, see the [Webhooks](/webhooks#webhook-signature-verification) page. *** ## Best Practices 1. **Restrict Bad Actors**: When processing a dispute webhook, use the `payment_id` to look up the original transaction in your system, resolve the associated player, and lock or restrict their account to prevent further fraud. 2. **Fraud Pattern Analysis**: Regularly audit dispute rates against the originating payments (joined via `payment_id`) to tune your checkout and fraud verification gates. 3. **Webhook Verification**: Always verify webhook signatures (see [Webhooks](/webhooks)) before executing player account restrictions. # Getting Started Source: https://docs.toffeepay.com/getting-started Integrate ToffeePay in minutes This guide walks you through: 1. [Setting up](#setup) 2. [Creating a payment session](#create-a-payment-session) 3. [Opening the payment page](#open-the-payment-page) 4. [Handling payment webhook](#handle-payment-webhook) 5. [Returning to game](#return-to-game) 6. [Confirming payment](#confirming-payment) For a visual overview of the complete payment flow, see the [Payments](/payments#payment-flow) page. *** ## Setup ```bash theme={null} npm install @toffeepay/sdk ``` ```typescript theme={null} import { Toffee } from "@toffeepay/sdk"; const toffee = new Toffee({ accessToken: "your-access-token", environment: "sandbox", // omit for production }); ``` ```bash theme={null} pip install toffeepay-sdk ``` ```python theme={null} from toffeepay import Toffee toffee = Toffee( access_token="your-access-token", environment="sandbox", # omit for production ) ``` ```bash theme={null} go get github.com/toffeepay/sdk-go ``` ```go theme={null} import sdk "github.com/toffeepay/sdk-go" toffee := sdk.New(sdk.Config{ AccessToken: "your-access-token", Environment: sdk.Sandbox, // omit for production }) ``` All requests to the ToffeePay API must include your access token in the `Authorization` header: ```http theme={null} Authorization: Bearer ``` See the [Authentication](/authentication) page for details on environments and base URLs. *** ## Create a Payment Session ```typescript theme={null} const session = await toffee.checkout({ gameId: "space_shooter", userId: "player_42", item: { title: "50 Gold Coins", price: 499, currency: "USD" }, returnUrl: "mygame://payment-complete", }); console.log(session.url); // redirect the user here ``` ```python theme={null} session = await toffee.checkout( game_id="space_shooter", user_id="player_42", item=Item(title="50 Gold Coins", price=499, currency="USD"), return_url="mygame://payment-complete", ) print(session.url) # redirect the user here ``` ```go theme={null} session, err := toffee.Checkout(ctx, &sdk.CreateSessionRequest{ GameId: "space_shooter", UserId: "player_42", Item: &sdk.Item{Title: "50 Gold Coins", Price: 499, Currency: "USD"}, ReturnUrl: "mygame://payment-complete", }) fmt.Println(session.GetUrl()) // redirect the user here ``` ```http theme={null} POST /v1/sessions Authorization: Bearer Content-Type: application/json { "game_id": "space_shooter", "user_id": "player_42", "item": { "title": "50 Gold Coins", "price": 499, "currency": "USD" }, "return_url": "mygame://payment-complete" } ``` See the [Create Session](/api-reference/sessions/create) API reference for the full request/response schema. *** ## Open the Payment Page Open the returned payment `url` in the browser. The user will: * View the list of items and total price * Pay using Apple Pay or another supported method *** ## Handle Payment Webhook Once payment is successful, ToffeePay sends a [`payment.succeeded`](/api-reference/webhooks/payment.succeeded) webhook to your backend. This is the most reliable way to confirm payments. See the [Webhooks](/webhooks) page for signature verification and all available events. *** ## Return to Game After a successful payment, the player is **redirected to your specified `return_url`**. This is typically a **custom deep link** that your game handles to: * Show a success screen * Confirm the session was paid **Example:** ```json theme={null} "return_url": "mygame://payment-complete" ``` Make sure your mobile client is set up to **handle and parse this URI scheme**. *** ## Confirming Payment To confirm the payment status after returning to the game: ```typescript theme={null} const status = await toffee.sessions.status({ id: "sess_123" }); if (status.status === "paid") { // Grant items } ``` ```python theme={null} resp = await toffee.sessions.status(GetSessionStatusRequest(id="sess_123")) if resp.status == "paid": # Grant items ``` ```go theme={null} resp, err := toffee.Sessions.Status(ctx, &sdk.GetSessionStatusRequest{Id: "sess_123"}) if resp.GetStatus() == "paid" { // Grant items } ``` ```http theme={null} GET /v1/sessions/sess_123/status Authorization: Bearer ``` See the [Payments](/payments#check-session-status) page for status handling details. # Idempotency Source: https://docs.toffeepay.com/idempotency Prevent duplicate operations Idempotency ensures that retrying the same request multiple times has the same effect as making it once. This is crucial for preventing duplicate payments, refunds, and other operations in unreliable network conditions. ## Idempotency-Key Header ToffeePay supports idempotency for mutation operations (POST requests) using the `Idempotency-Key` header: ```http theme={null} POST /v1/sessions Authorization: Bearer Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 Content-Type: application/json ... ``` ## How It Works 1. **First Request**: ToffeePay processes the request and stores the result with the idempotency key 2. **Duplicate Requests**: If the same idempotency key is used again within 24 hours, ToffeePay returns the cached result instead of processing again 3. **Different Parameters**: If the same key is used with different request parameters, ToffeePay returns an error ## Supported Operations Idempotency is supported for these operations: * [Create Session](/api-reference/sessions/create) - Prevents duplicate payment sessions * [Create Refund](/api-reference/refunds/create) - Prevents duplicate refunds ## Best Practices ### Generate Unique Keys Use UUIDs or other collision-resistant identifiers: ```typescript theme={null} import { v4 as uuidv4 } from 'uuid'; const idempotencyKey = uuidv4(); // e.g., "550e8400-e29b-41d4-a716-446655440000" ``` ### Key Per Operation Use a different idempotency key for each distinct operation: ```typescript theme={null} // Good: Different keys for different operations const sessionKey = `session-${userId}-${itemId}-${Date.now()}`; const refundKey = `refund-${paymentId}-${Date.now()}`; // Bad: Reusing the same key across different operations const key = "my-operation-key"; // Don't do this ``` ## Error Handling ### Idempotency Conflicts If you use the same idempotency key with different parameters: ```http theme={null} HTTP/1.1 409 Conflict Content-Type: application/json { "error": { "code": "idempotency_conflict", "message": "Idempotency key already used with different parameters" } } ``` ### Key Expiration Idempotency keys expire after 24 hours. After expiration, the same key can be reused. ## Webhook Idempotency For webhook processing, use the `webhook-id` header to deduplicate events. See the [Webhooks](/webhooks#idempotency) page for details. # Introduction Source: https://docs.toffeepay.com/introduction Payment processing platform for games ## What is ToffeePay? ToffeePay is a comprehensive payment solution that allows games to: * Accept payments through multiple channels * Process transactions securely * Manage refunds and disputes * Analyze payment data and trends ## Key Features * **Multi-channel payments**: Accept payments via Apple Pay, Google Pay, Cards, PayPal and Venmo. * **Toffee Wallet**: Integrated wallet system with mobile app for balance management, transaction history and cashback rewards * **Global support**: Process payments in multiple currencies * **Developer-friendly API**: Easy integration with clean APIs and SDKs * **Enhanced security**: PCI-DSS compliant with advanced fraud protection * **Real-time reporting**: Comprehensive analytics and reporting tools * **Webhook notifications**: Stay updated with real-time payment status changes # Payments Source: https://docs.toffeepay.com/payments Sessions, statuses and payment flow ## Payment Flow ```mermaid theme={null} sequenceDiagram participant Game as Game participant Server as Game Server participant API as ToffeePay participant Page as Payment Page Game->>Server: Request payment for item Server->>API: Create Session request API->>Server: Returns `session` object with `id` & `url` Server->>Game: Send `session.url` Game->>Page: Open `session.url` in browser Page->>Page: User completes payment par Webhook Flow API->>Server: Send webhook (signed) Server->>Server: Verify webhook signature Server->>Server: Process payment & grant items and Return URL Flow Page->>Game: Redirect to return_url (deep link) Game->>Server: Use existing session_id, request status Server->>API: Get Session API->>Server: Return payment status Server->>Game: Confirm payment & show items end ``` *** ## Available Payment Methods ToffeePay supports the following payment methods: * **card**: Traditional credit/debit card payments * **tokenized\_card**: Saved card tokens for returning customers * **apple\_pay**: Apple Pay for iOS and Safari users * **google\_pay**: Google Pay for Android and Chrome users * **paypal**: PayPal wallet payments * **paylater**: PayPal's buy now, pay later options * **venmo**: Venmo wallet payments The available payment methods are automatically displayed to users on the payment page based on their device, browser, and configured options for your game. *** ## Create a Payment Session Send a server-side request to [create a payment session](/api-reference/sessions/create). ```typescript theme={null} const { session } = await toffee.sessions.create({ gameId: "space_shooter", userId: "player_42", item: { title: "50 Gold Coins", price: 499, currency: "USD", image: "data:image/png;base64,...", }, returnUrl: "mygame://payment-complete", walletEnabled: true, }); ``` ```python theme={null} resp = await toffee.sessions.create(CreateSessionRequest( game_id="space_shooter", user_id="player_42", item=Item( title="50 Gold Coins", price=499, currency="USD", image="data:image/png;base64,...", ), return_url="mygame://payment-complete", wallet_enabled=True, )) session = resp.session ``` ```go theme={null} resp, err := toffee.Sessions.Create(ctx, &sdk.CreateSessionRequest{ GameId: "space_shooter", UserId: "player_42", Item: &sdk.Item{ Title: "50 Gold Coins", Price: 499, Currency: "USD", Image: "data:image/png;base64,...", }, ReturnUrl: "mygame://payment-complete", WalletEnabled: true, }) session := resp.GetSession() ``` ```http theme={null} POST /v1/sessions Authorization: Bearer Content-Type: application/json { "game_id": "space_shooter", "user_id": "player_42", "item": { "title": "50 Gold Coins", "price": 499, "currency": "USD", "image": "data:image/png;base64,..." }, "return_url": "mygame://payment-complete", "wallet_enabled": true } ``` **Parameters:** * `game_id`: Your unique game identifier * `user_id`: Unique identifier for the player * `item`: The item being purchased * `title`: Display name of the item * `price`: Price in cents (e.g., 499 = \\\$4.99) * `currency`: Three-letter currency code (USD, EUR, etc.) * `image`: Base64-encoded image or URL * `return_url`: Deep link to return to after payment * `wallet_enabled`: Boolean flag that specifies whether [Toffee Wallet](/wallet) should be enabled or disabled for the session **Response:** A Session object with the payment URL. ```json theme={null} { "id": "sess_abc123", "url": "https://pay.toffeepay.com/sess_abc123", "status": "pending", "user_id": "player_42", "game_id": "space_shooter", "item": { "title": "50 Gold Coins", "price": 499, "currency": "USD" }, "created_at": "2023-06-01T12:00:00Z" } ``` *** ## Payment Statuses ### Session Statuses Payment sessions can have the following statuses: * `pending`: Payment session created but not yet paid * `paid`: Payment successfully completed * `failed`: Payment attempt failed * `cancelled`: Payment session was cancelled * `expired`: Payment session has expired ### Payment Statuses Individual payment objects (created when processing a session) have these statuses: * `processing`: Payment is being processed by the payment provider * `authorized`: Payment has been authorized but not yet captured * `succeeded`: Payment completed successfully * `cancelled`: Authorized payment was cancelled before capture * `failed`: Payment processing failed **Status Flow:** 1. Session starts as `pending` 2. When payment begins, a Payment object is created with status `processing` 3. Payment can transition to: * `authorized`: Payment authorized but not captured * `succeeded`: Payment completed successfully (either automatically or via [Complete Payment](/api-reference/payments/complete)) * `cancelled`: Authorized payment was cancelled via [Cancel Payment](/api-reference/payments/cancel) * `failed`: Payment processing failed **Note:** Whether payments automatically proceed from `authorized` to `succeeded` or require manual completion via [Complete Payment](/api-reference/payments/complete) is configured during game setup. 4. Session status updates based on final payment status: * `paid` (if payment succeeded) * `cancelled` (if session was cancelled manually via [Cancel Session](/api-reference/sessions/cancel) or after 3 cancelled payment attempts) * `failed` (after 3 failed payment attempts) ### Timestamp Fields Both sessions and payments include relevant timestamp fields: **Session timestamps:** * `created_at`: When the session was created * `paid_at`: When the session was successfully paid (if applicable) * `failed_at`: When the session failed (if applicable) * `expired_at`: When the session expired (if applicable) * `cancelled_at`: When the session was cancelled (if applicable) **Payment timestamps:** * `created_at`: When payment processing began * `authorized_at`: When payment was authorized (if applicable) * `succeeded_at`: When payment completed successfully (if applicable) * `cancelled_at`: When payment was cancelled (if applicable) * `failed_at`: When payment failed (if applicable) *** ## Webhook Events ToffeePay sends webhooks for the following payment-related events: ### Session Events * [`session.created`](/api-reference/webhooks/session.created): When a payment session is created * [`session.paid`](/api-reference/webhooks/session.paid): When a session is successfully paid * [`session.failed`](/api-reference/webhooks/session.failed): When payment attempt related to this session fails * [`session.expired`](/api-reference/webhooks/session.expired): When a session expires without payment * [`session.cancelled`](/api-reference/webhooks/session.cancelled): When session is cancelled by user ### Payment Events * [`payment.created`](/api-reference/webhooks/payment.created): When a payment is created and processing begins * [`payment.authorized`](/api-reference/webhooks/payment.authorized): When payment is authorized but not yet captured * [`payment.succeeded`](/api-reference/webhooks/payment.succeeded): When payment completes successfully * [`payment.cancelled`](/api-reference/webhooks/payment.cancelled): When an authorized payment is cancelled * [`payment.failed`](/api-reference/webhooks/payment.failed): When payment attempt fails See the [Webhooks](/webhooks) page for implementation details and signature verification. *** ## Open the Payment Page Open the returned payment `url` in the browser or webview. The user will: * View the item details and total price * Pay using Apple Pay, Google Pay, or other supported methods * Be redirected back to your game via the `return_url` ### Internationalization You can customize the payment page language by adding a `locale` query parameter to the payment URL: ``` https://pay.toffeepay.com/sess_abc123?locale=es ``` The payment page will display all text (buttons, labels, error messages) in the specified language. *** ## Handle Payment Notifications ToffeePay notifies you of payment events through two methods: ### Webhooks When a payment is completed, ToffeePay sends a signed webhook to your backend. See the [Webhooks](/webhooks) page for detailed implementation. **Sample Payload:** ```json theme={null} { "type": "payment.succeeded", "timestamp": "2023-06-01T12:05:00Z", "data": { // Payment object } } ``` ### Return URL After payment, the user is redirected to your specified `return_url`. This is typically a custom deep link that your game handles. **Example:** ```json theme={null} "return_url": "mygame://payment-complete" ``` ### Frontend Events The payment page emits events to the parent window via `postMessage`. This is useful for client-side integrations (e.g., if using an iframe or popup). **Event Types:** **Payment Events** (payload includes payment `id`): * `toffeepay-payment-unreached` * `toffeepay-payment-success` * `toffeepay-payment-failed` **Session Events** (payload includes session `id`): * `toffeepay-session-paid` * `toffeepay-session-failed` * `toffeepay-session-expired` * `toffeepay-session-cancelled` * `toffeepay-session-authorized` *** ## Cancel a Payment Session You can [cancel a payment session](/api-reference/sessions/cancel) that is still in `pending` status: ```typescript theme={null} await toffee.sessions.cancel({ id: "sess_abc123" }); ``` ```python theme={null} await toffee.sessions.cancel(CancelSessionRequest(id="sess_abc123")) ``` ```go theme={null} err := toffee.Sessions.Cancel(ctx, &sdk.CancelSessionRequest{Id: "sess_abc123"}) ``` ```http theme={null} POST /v1/sessions/sess_abc123/cancel Authorization: Bearer ``` When a session is cancelled: * The session status changes to `cancelled` * The `cancelled_at` timestamp is set * A `session.cancelled` webhook event is sent * The session cannot be paid and the payment URL becomes invalid *** ## Check Session Status To confirm session status (especially when handling return URLs), you can use either endpoint: ### [Get Session](/api-reference/sessions/get) (Full Details) ```typescript theme={null} const { session } = await toffee.sessions.get({ id: "sess_abc123" }); ``` ```python theme={null} resp = await toffee.sessions.get(GetSessionRequest(id="sess_abc123")) session = resp.session ``` ```go theme={null} resp, err := toffee.Sessions.Get(ctx, &sdk.GetSessionRequest{Id: "sess_abc123"}) session := resp.GetSession() ``` ```http theme={null} GET /v1/sessions/sess_abc123 Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "sess_abc123", "url": "https://pay.toffeepay.com/sess_abc123", "status": "paid", "user_id": "player_42", "game_id": "space_shooter", "item": { "title": "50 Gold Coins", "price": 499, "currency": "USD" }, "tax": { "amount": 50 }, "created_at": "2023-06-01T12:00:00Z", "paid_at": "2023-06-01T12:05:00Z" } ``` ### [Get Session Status](/api-reference/sessions/status) (Status Only) ```typescript theme={null} const { status } = await toffee.sessions.status({ id: "sess_abc123" }); ``` ```python theme={null} resp = await toffee.sessions.status(GetSessionStatusRequest(id="sess_abc123")) status = resp.status ``` ```go theme={null} resp, err := toffee.Sessions.Status(ctx, &sdk.GetSessionStatusRequest{Id: "sess_abc123"}) status := resp.GetStatus() ``` ```http theme={null} GET /v1/sessions/sess_abc123/status Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "sess_abc123", "status": "paid" } ``` **Status handling:** * If `status` is `paid`, grant the items and show success * If `status` is `pending`, show a waiting screen * If `status` is `failed`, `cancelled` or `expired`, inform the user and offer to retry *** ## Complete Payment When your game wants to control when an authorized payment is captured, use [Complete Payment](/api-reference/payments/complete). This is useful for scenarios where you want to authorize payment but only capture it after fulfilling the order (e.g., confirming item availability). ```typescript theme={null} await toffee.payments.complete({ id: "pay_xyz789" }); ``` ```python theme={null} await toffee.payments.complete(CompletePaymentRequest(id="pay_xyz789")) ``` ```go theme={null} err := toffee.Payments.Complete(ctx, &sdk.CompletePaymentRequest{Id: "pay_xyz789"}) ``` ```http theme={null} POST /v1/payments/pay_xyz789/complete Authorization: Bearer ``` *** ## Cancel Payment [Cancel an authorized payment](/api-reference/payments/cancel) before it's captured. This releases the hold on the customer's payment method. ```typescript theme={null} await toffee.payments.cancel({ id: "pay_xyz789" }); ``` ```python theme={null} await toffee.payments.cancel(CancelPaymentRequest(id="pay_xyz789")) ``` ```go theme={null} err := toffee.Payments.Cancel(ctx, &sdk.CancelPaymentRequest{Id: "pay_xyz789"}) ``` ```http theme={null} POST /v1/payments/pay_xyz789/cancel Authorization: Bearer ``` *** ## Get Payment Details [Retrieve details](/api-reference/payments/get) of a payment: ```typescript theme={null} const { payment } = await toffee.payments.get({ id: "pay_xyz789" }); ``` ```python theme={null} resp = await toffee.payments.get(GetPaymentRequest(id="pay_xyz789")) payment = resp.payment ``` ```go theme={null} resp, err := toffee.Payments.Get(ctx, &sdk.GetPaymentRequest{Id: "pay_xyz789"}) payment := resp.GetPayment() ``` ```http theme={null} GET /v1/payments/pay_xyz789?with_extra_data=true Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "pay_xyz789", "session_id": "sess_abc123", "status": "succeeded", "amount": 499, "currency": "USD", "method": "apple_pay", "details": "50 Gold Coins", "created_at": "2023-06-01T12:05:00Z", "succeeded_at": "2023-06-01T12:05:30Z", "extra_data": { "real_amount": 499 } } ``` *** ## List Sessions [Retrieve historical sessions](/api-reference/sessions/list) for audit and analytics: ```typescript theme={null} const { sessions } = await toffee.sessions.list({ gameId: "space_shooter", userId: "player_42", }); ``` ```python theme={null} resp = await toffee.sessions.list(ListSessionsRequest( game_id="space_shooter", user_id="player_42", )) sessions = resp.sessions ``` ```go theme={null} resp, err := toffee.Sessions.List(ctx, &sdk.ListSessionsRequest{ GameId: "space_shooter", UserId: "player_42", }) sessions := resp.GetSessions() ``` ```http theme={null} GET /v1/sessions?game_id=space_shooter&user_id=player_42&status=paid&limit=50&offset=0 Authorization: Bearer ``` **Response:** ```json theme={null} [ { "id": "sess_abc123", "status": "paid", "user_id": "player_42", "game_id": "space_shooter", "item": { "title": "50 Gold Coins", "price": 499, "currency": "USD" }, "tax": { "amount": 50 }, "created_at": "2023-06-01T12:00:00Z", "paid_at": "2023-06-01T12:05:00Z" } ] ``` ### Filtering by Date You can use the `from` and `to` parameters (in RFC 3339 format) to retrieve records created within a specific timeframe. This works identically across both Sessions and Payments. ```typescript theme={null} const { sessions } = await toffee.sessions.list({ gameId: "space_shooter", from: "2024-05-01T00:00:00Z", to: "2024-05-31T23:59:59Z", }); ``` ```python theme={null} resp = await toffee.sessions.list(ListSessionsRequest( game_id="space_shooter", from="2024-05-01T00:00:00Z", to="2024-05-31T23:59:59Z", )) ``` ```go theme={null} resp, err := toffee.Sessions.List(ctx, &sdk.ListSessionsRequest{ GameId: "space_shooter", From: timestamppb.New(time.Date(2024, 5, 1, 0, 0, 0, 0, time.UTC)), To: timestamppb.New(time.Date(2024, 5, 31, 23, 59, 59, 0, time.UTC)), }) ``` ```http theme={null} GET /v1/sessions?game_id=space_shooter&from=2024-05-01T00:00:00Z&to=2024-05-31T23:59:59Z Authorization: Bearer ``` ```bash theme={null} curl -X POST https://api.toffeepay.com/pay.v1.PaymentService/ListSessions -H "Content-Type: application/json" -H "Authorization: Bearer " -d '{ "game_id": "space_shooter", "from": "2024-05-01T00:00:00Z", "to": "2024-05-31T23:59:59Z" }' ``` *** ## List Payments [Retrieve historical payments](/api-reference/payments/list) (successful transactions only): ```typescript theme={null} const { payments } = await toffee.payments.list({ gameId: "space_shooter", userId: "player_42", }); ``` ```python theme={null} resp = await toffee.payments.list(ListPaymentsRequest( game_id="space_shooter", user_id="player_42", )) payments = resp.payments ``` ```go theme={null} resp, err := toffee.Payments.List(ctx, &sdk.ListPaymentsRequest{ GameId: "space_shooter", UserId: "player_42", }) payments := resp.GetPayments() ``` ```http theme={null} GET /v1/payments?game_id=space_shooter&user_id=player_42&limit=50&offset=0&with_extra_data=true Authorization: Bearer ``` **Response:** ```json theme={null} [ { "id": "pay_xyz789", "session_id": "sess_abc123", "status": "succeeded", "amount": 499, "currency": "USD", "method": "apple_pay", "details": "50 Gold Coins", "created_at": "2023-06-01T12:05:00Z", "succeeded_at": "2023-06-01T12:05:30Z", "extra_data": { "real_amount": 499 } } ] ``` *** ## Tax Handling ToffeePay automatically calculates and applies taxes based on the customer's location. Tax information is included in session responses when the session is completed. ### Tax Structure The tax object in session responses contains: * `amount`: Tax amount in cents (same currency as the item price) ### Example For a \\$4.99 item with \\$0.50 tax: ```json theme={null} { "item": { "price": 499, "currency": "USD" }, "tax": { "amount": 50 } } ``` The customer will be charged the total amount (item price + tax = \\\$5.49 in this example). **Note:** Tax calculations are handled automatically during payment processing. The tax amount is determined at the time of payment based on the customer's billing address. *** ## Image Requirements Item images must meet these requirements: * **Format**: PNG, JPG, WebP * **Size**: ≤ 300 KB recommended * **Encoding**: Base64 with MIME prefix or direct URL **Base64 format:** ``` data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... ``` **URL format:** ``` https://yourgame.com/images/gold-coins.png ``` *** ## Best Practices 1. **Authentication**: See [Authentication](/authentication) for access token management 2. **Idempotency**: Use [Idempotency-Key](/idempotency) header to prevent duplicate operations 3. **Webhook verification**: Always verify webhook signatures (see [Webhooks](/webhooks)) 4. **Error handling**: Implement proper error handling for all payment statuses 5. **User experience**: Show loading states and clear success/failure messages 6. **Security**: Use HTTPS for all webhook endpoints and return URLs 7. **Testing**: Use sandbox mode for development and testing *** ## Error Handling Common payment errors and how to handle them: * `invalid_game_id`: Check your game registration and access token (see [Authentication](/authentication)) * `invalid_currency`: Ensure currency code is supported (USD, EUR, etc.) * `invalid_price`: Price must be a positive integer in cents * `invalid_image`: Check image format and size requirements * `session_expired`: Payment session has expired, create a new one * `insufficient_funds`: User's payment method was declined *** ## Testing Use the [Sandbox](/sandbox) environment for development and testing. Test cards and OTP codes are available there. # Refunds Source: https://docs.toffeepay.com/refunds Initiate and track refunds ## Refund Flow ```mermaid theme={null} sequenceDiagram participant Game as Game participant Server as Game Server participant API as ToffeePay Game->>Server: Request refund Server->>API: Create Refund API->>Server: Return refund object Server->>Game: Confirm refund initiated Note over API: Processing refund... API->>Server: Send refund webhook (signed) Server->>Server: Verify webhook signature Server->>Server: Process refund & revoke item Server->>Game: Notify refund completed ``` *** ## Create a Refund Send a server-side request to [create a refund](/api-reference/refunds/create) for a paid session. ```typescript theme={null} const { refund } = await toffee.refunds.create({ paymentId: "pay_xyz789", reason: "Customer request", }); ``` ```python theme={null} resp = await toffee.refunds.create(CreateRefundRequest( payment_id="pay_xyz789", reason="Customer request", )) refund = resp.refund ``` ```go theme={null} resp, err := toffee.Refunds.Create(ctx, &sdk.CreateRefundRequest{ PaymentId: "pay_xyz789", Reason: "Customer request", }) refund := resp.GetRefund() ``` ```http theme={null} POST /v1/refunds Authorization: Bearer Content-Type: application/json { "payment_id": "pay_xyz789", "reason": "Customer request" } ``` **Parameters:** * `payment_id`: The original payment ID to refund * `reason`: Optional reason for the refund **Response:** A Refund object. ```json theme={null} { "id": "ref_xyz789", "payment_id": "pay_xyz789", "status": "pending", "reason": "Customer request", "created_at": "2023-06-01T12:00:00Z" } ``` *** ## Refund Statuses Refunds can have the following statuses: * `pending`: Refund created but not yet processed * `succeeded`: Refund has been successfully processed and funds returned * `failed`: Refund failed (e.g., insufficient funds, payment method issues) * `cancelled`: Refund was cancelled before completion **Status Flow:** 1. Refund starts as `pending` 2. Refund resolves to either `succeeded`, `failed`, or `cancelled` ### Timestamp Fields Refunds include relevant timestamp fields based on their final status: * `created_at`: When the refund was initiated * `succeeded_at`: When the refund completed successfully (if applicable) * `failed_at`: When the refund failed (if applicable) * `cancelled_at`: When the refund was cancelled (if applicable) *** ## Webhook Events ToffeePay sends webhooks for the following refund-related events: * [`refund.created`](/api-reference/webhooks/refund.created): When a refund is created and processing begins * [`refund.succeeded`](/api-reference/webhooks/refund.succeeded): When a refund has been processed successfully * [`refund.failed`](/api-reference/webhooks/refund.failed): When a refund attempt fails See the [Webhooks](/webhooks) page for implementation details and signature verification. *** ## Handle Refund Notifications When a refund is processed, ToffeePay sends a **signed webhook** to your backend. **Sample Payload:** ```json theme={null} { "type": "refund.succeeded", "timestamp": "2023-06-01T12:10:00Z", "data": { // Refund object } } ``` For webhook signature verification, see the [Webhooks](/webhooks#webhook-signature-verification) page. *** ## Check Refund Status You can [check the status](/api-reference/refunds/get) of a refund at any time: ```typescript theme={null} const { refund } = await toffee.refunds.get({ id: "ref_xyz789" }); ``` ```python theme={null} resp = await toffee.refunds.get(GetRefundRequest(id="ref_xyz789")) refund = resp.refund ``` ```go theme={null} resp, err := toffee.Refunds.Get(ctx, &sdk.GetRefundRequest{Id: "ref_xyz789"}) refund := resp.GetRefund() ``` ```http theme={null} GET /v1/refunds/ref_xyz789 Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "ref_xyz789", "payment_id": "pay_xyz789", "status": "succeeded", "reason": "Customer request", "created_at": "2023-06-01T12:00:00Z", "succeeded_at": "2023-06-01T12:05:00Z" } ``` *** ## List Refunds [Retrieve historical refunds](/api-reference/refunds/list) for audit and analytics: ```typescript theme={null} const { refunds } = await toffee.refunds.list({ paymentId: "pay_xyz789" }); ``` ```python theme={null} resp = await toffee.refunds.list(ListRefundsRequest(payment_id="pay_xyz789")) refunds = resp.refunds ``` ```go theme={null} paymentId := "pay_xyz789" resp, err := toffee.Refunds.List(ctx, &sdk.ListRefundsRequest{ PaymentId: &paymentId, }) refunds := resp.GetRefunds() ``` ```http theme={null} GET /v1/refunds?payment_id=pay_xyz789&status=succeeded&limit=50&offset=0 Authorization: Bearer ``` **Response:** ```json theme={null} [ { "id": "ref_xyz789", "payment_id": "pay_xyz789", "status": "succeeded", "reason": "Customer request", "created_at": "2023-06-01T12:00:00Z", "succeeded_at": "2023-06-01T12:05:00Z" } ] ``` *** ## Best Practices 1. **Idempotency**: Use [Idempotency-Key](/idempotency) header to prevent duplicate refunds 2. **Revoke Items**: When processing a refund webhook, revoke the purchased items from the user's account 3. **Webhook verification**: Always verify webhook signatures (see [Webhooks](/webhooks)) # Sandbox Source: https://docs.toffeepay.com/sandbox Test your integration without real payments The ToffeePay sandbox is a fully isolated environment for development and testing. It behaves identically to production but no real payments are processed. ## Getting Access 1. Register at [sandbox.dashboard.toffeepay.com](https://sandbox.dashboard.toffeepay.com/) 2. Complete the onboarding process to set up your game 3. Copy your sandbox access token from the dashboard Use this token with the sandbox base URL: ``` https://api.sandbox.toffeepay.com ``` Or via SDK: ```typescript theme={null} const toffee = new Toffee({ accessToken: "your-sandbox-token", environment: "sandbox", }); ``` ```python theme={null} toffee = Toffee( access_token="your-sandbox-token", environment="sandbox", ) ``` ```go theme={null} toffee := sdk.New(sdk.Config{ AccessToken: "your-sandbox-token", Environment: sdk.Sandbox, }) ``` *** ## Test Cards Use these card numbers to simulate different payment scenarios: **Mastercard** * Number: `5100 0600 0000 0002` * Expiry: `12/2029` * CVC: `737` **Visa** * Number: `4400 0000 0000 0008` * Expiry: `03/2030` * CVC: `737` *** ## Test OTP We don't send real SMS messages in the sandbox. Use the static OTP code: **`466577`** *** ## Webhook Testing Use a tool like [ngrok](https://ngrok.com/) to expose your local server to the sandbox: ```bash theme={null} ngrok http 3000 ``` Then set your webhook URL in the dashboard to the ngrok URL. *** ## Notes * Sandbox and production use **separate access tokens** — they are not interchangeable * Sandbox data is isolated and does not affect production * Payment methods like Apple Pay and Google Pay are simulated — use test cards instead # Go Source: https://docs.toffeepay.com/sdks/go SDK for Go ## Install ```bash theme={null} go get github.com/toffeepay/sdk-go ``` ## Configuration ```go theme={null} import sdk "github.com/toffeepay/sdk-go" toffee := sdk.New(sdk.Config{ AccessToken: "your-access-token", }) ``` ### Sandbox ```go theme={null} toffee := sdk.New(sdk.Config{ AccessToken: "your-sandbox-token", Environment: sdk.Sandbox, }) ``` ## Checkout Shorthand for creating a payment session: ```go theme={null} session, err := toffee.Checkout(ctx, &sdk.CreateSessionRequest{ Item: &sdk.Item{Title: "Gem Pack 50", Price: 100, Currency: "USD"}, GameId: "your-game-id", UserId: "user-id", ReturnUrl: "https://example.com/return", }) fmt.Println(session.GetUrl()) // redirect the user here ``` ## Refund Shorthand for creating a refund: ```go theme={null} refund, err := toffee.Refund(ctx, &sdk.CreateRefundRequest{ PaymentId: "pay_123", }) ``` ## Idempotency Pass an idempotency key to protect against duplicate operations: ```go theme={null} session, err := toffee.Checkout(ctx, &sdk.CreateSessionRequest{ Item: &sdk.Item{Title: "Gem Pack 50", Price: 100, Currency: "USD"}, GameId: "game_1", UserId: "user_1", ReturnUrl: "https://example.com/return", }, sdk.WithIdempotencyKey("unique-key-123")) refund, err := toffee.Refund(ctx, &sdk.CreateRefundRequest{ PaymentId: "pay_123", }, sdk.WithIdempotencyKey("refund-key-456")) ``` ## Resources ### Sessions ```go theme={null} resp, err := toffee.Sessions.Create(ctx, &sdk.CreateSessionRequest{...}) session := resp.GetSession() resp, err := toffee.Sessions.Get(ctx, &sdk.GetSessionRequest{Id: "sess_123"}) session := resp.GetSession() resp, err := toffee.Sessions.Status(ctx, &sdk.GetSessionStatusRequest{Id: "sess_123"}) // resp.GetId(), resp.GetStatus() resp, err := toffee.Sessions.List(ctx, &sdk.ListSessionsRequest{GameId: "game_1"}) sessions := resp.GetSessions() _, err = toffee.Sessions.Cancel(ctx, &sdk.CancelSessionRequest{Id: "sess_123"}) ``` ### Payments ```go theme={null} resp, err := toffee.Payments.Get(ctx, &sdk.GetPaymentRequest{Id: "pay_123"}) payment := resp.GetPayment() resp, err := toffee.Payments.List(ctx, &sdk.ListPaymentsRequest{GameId: "game_1"}) payments := resp.GetPayments() _, err = toffee.Payments.Complete(ctx, &sdk.CompletePaymentRequest{Id: "pay_123"}) _, err = toffee.Payments.Cancel(ctx, &sdk.CancelPaymentRequest{Id: "pay_123"}) ``` ### Refunds ```go theme={null} resp, err := toffee.Refunds.Create(ctx, &sdk.CreateRefundRequest{PaymentId: "pay_123"}) refund := resp.GetRefund() resp, err := toffee.Refunds.Get(ctx, &sdk.GetRefundRequest{Id: "ref_123"}) refund := resp.GetRefund() resp, err := toffee.Refunds.List(ctx, &sdk.ListRefundsRequest{PaymentId: &paymentId}) refunds := resp.GetRefunds() ``` ### Accounts ```go theme={null} resp, err := toffee.Accounts.Get(ctx, &sdk.GetAccountRequest{Id: "acc_123"}) account := resp.GetAccount() resp, err := toffee.Accounts.List(ctx, &sdk.ListAccountsRequest{GameId: "game_1"}) accounts := resp.GetAccounts() ``` ### Deposits ```go theme={null} resp, err := toffee.Deposits.Get(ctx, &sdk.GetDepositRequest{Id: "dep_123"}) deposit := resp.GetDeposit() resp, err := toffee.Deposits.List(ctx, &sdk.ListDepositsRequest{AccountId: "acc_123"}) deposits := resp.GetDeposits() ``` # Python Source: https://docs.toffeepay.com/sdks/python SDK for Python ## Install ```bash theme={null} pip install toffeepay-sdk ``` ## Configuration ```python theme={null} from toffeepay import Toffee toffee = Toffee(access_token="your-access-token") ``` ### Sandbox ```python theme={null} toffee = Toffee(access_token="your-sandbox-token", environment="sandbox") ``` ## Checkout Shorthand for creating a payment session: ```python theme={null} from toffeepay import Item session = await toffee.checkout( game_id="your-game-id", user_id="user-id", item=Item(title="Gem Pack 50", price=100, currency="USD"), return_url="https://example.com/return", ) print(session.url) # redirect the user here ``` ## Refund Shorthand for creating a refund: ```python theme={null} refund = await toffee.refund(payment_id="pay_123") ``` ## Idempotency Pass an idempotency key to protect against duplicate operations: ```python theme={null} session = await toffee.checkout( game_id="game_1", user_id="user_1", item=Item(title="Gem Pack 50", price=100, currency="USD"), return_url="https://example.com/return", options=RequestOptions(idempotency_key="unique-key-123"), ) refund = await toffee.refund( payment_id="pay_123", options=RequestOptions(idempotency_key="refund-key-456"), ) ``` ## Resources ### Sessions ```python theme={null} resp = await toffee.sessions.create(CreateSessionRequest(...)) session = resp.session resp = await toffee.sessions.get(GetSessionRequest(id="sess_123")) session = resp.session resp = await toffee.sessions.status(GetSessionStatusRequest(id="sess_123")) status = resp.status resp = await toffee.sessions.list(ListSessionsRequest(game_id="game_1")) sessions = resp.sessions await toffee.sessions.cancel(CancelSessionRequest(id="sess_123")) ``` ### Payments ```python theme={null} resp = await toffee.payments.get(GetPaymentRequest(id="pay_123")) payment = resp.payment resp = await toffee.payments.list(ListPaymentsRequest(game_id="game_1")) payments = resp.payments await toffee.payments.complete(CompletePaymentRequest(id="pay_123")) await toffee.payments.cancel(CancelPaymentRequest(id="pay_123")) ``` ### Refunds ```python theme={null} resp = await toffee.refunds.create(CreateRefundRequest(payment_id="pay_123")) refund = resp.refund resp = await toffee.refunds.get(GetRefundRequest(id="ref_123")) refund = resp.refund resp = await toffee.refunds.list(ListRefundsRequest(payment_id="pay_123")) refunds = resp.refunds ``` ### Accounts ```python theme={null} resp = await toffee.accounts.get(GetAccountRequest(id="acc_123")) account = resp.account resp = await toffee.accounts.list(ListAccountsRequest(game_id="game_1")) accounts = resp.accounts ``` ### Deposits ```python theme={null} resp = await toffee.deposits.get(GetDepositRequest(id="dep_123")) deposit = resp.deposit resp = await toffee.deposits.list(ListDepositsRequest(account_id="acc_123")) deposits = resp.deposits ``` # TypeScript Source: https://docs.toffeepay.com/sdks/typescript SDK for TypeScript/JavaScript ## Install ```bash theme={null} npm install @toffeepay/sdk ``` ## Configuration ```typescript theme={null} import { Toffee } from "@toffeepay/sdk"; const toffee = new Toffee({ accessToken: "your-access-token", }); ``` ### Sandbox ```typescript theme={null} const toffee = new Toffee({ accessToken: "your-sandbox-token", environment: "sandbox", }); ``` ## Checkout Shorthand for creating a payment session: ```typescript theme={null} const session = await toffee.checkout({ item: { title: "Gem Pack 50", price: 100, currency: "USD" }, gameId: "your-game-id", userId: "user-id", returnUrl: "https://example.com/return", }); console.log(session.url); // redirect the user here ``` ## Refund Shorthand for creating a refund: ```typescript theme={null} const refund = await toffee.refund({ paymentId: "pay_123" }); ``` ## Idempotency Pass an idempotency key to protect against duplicate operations: ```typescript theme={null} const session = await toffee.checkout( { item: { title: "Gem Pack 50", price: 100, currency: "USD" }, gameId: "game_1", userId: "user_1", returnUrl: "https://example.com/return", }, { idempotencyKey: "unique-key-123" }, ); const refund = await toffee.refund( { paymentId: "pay_123" }, { idempotencyKey: "refund-key-456" }, ); ``` ## Resources ### Sessions ```typescript theme={null} const { session } = await toffee.sessions.create({ ... }); const { session } = await toffee.sessions.get({ id: "sess_123" }); const { status } = await toffee.sessions.status({ id: "sess_123" }); const { sessions } = await toffee.sessions.list({ gameId: "game_1" }); await toffee.sessions.cancel({ id: "sess_123" }); ``` ### Payments ```typescript theme={null} const { payment } = await toffee.payments.get({ id: "pay_123" }); const { payments } = await toffee.payments.list({ gameId: "game_1" }); await toffee.payments.complete({ id: "pay_123" }); await toffee.payments.cancel({ id: "pay_123" }); ``` ### Refunds ```typescript theme={null} const { refund } = await toffee.refunds.create({ paymentId: "pay_123" }); const { refund } = await toffee.refunds.get({ id: "ref_123" }); const { refunds } = await toffee.refunds.list({ paymentId: "pay_123" }); ``` ### Accounts ```typescript theme={null} const { account } = await toffee.accounts.get({ id: "acc_123" }); const { accounts } = await toffee.accounts.list({ gameId: "game_1" }); ``` ### Deposits ```typescript theme={null} const { deposit } = await toffee.deposits.get({ id: "dep_123" }); const { deposits } = await toffee.deposits.list({ gameId: "game_1" }); ``` # Wallet Source: https://docs.toffeepay.com/wallet Wallet, accounts and deposits ## What is Toffee Wallet? **Toffee Wallet** is a service that includes a mobile app providing access to the user's account, allowing them to view and top up their balance, check their transaction history, and add wallet passes, etc. It also serves as a payment provider that can be enabled for a game or group of games and provides: * Creation of a **Toffee Wallet** account for the user (with the user's consent) * Cashback accumulation * Ability to pay with **Toffee Wallet** on the checkout page Contact the Toffee Team to configure and enable **Toffee Wallet** functionality for your game or group of games. Configurable options: * Account asset (a card-like image that will be displayed in the **Toffee Wallet** app) * Wallet Pass assets * Cashback percentage * Top-up amounts and their associated rewards ## App links #### iOS [https://apps.apple.com/us/app/toffee-wallet/id6751423503](https://apps.apple.com/us/app/toffee-wallet/id6751423503) #### Android [https://play.google.com/store/apps/details?id=com.toffee.wallet](https://play.google.com/store/apps/details?id=com.toffee.wallet) ## Configure Toffee Wallet per session To enable or disable **Toffee Wallet** per session, you can use the `wallet_enabled` parameter in the [Create Session](/api-reference/sessions/create) request. After **Toffee Wallet** is configured and enabled for your game or group of games, it is also **enabled** by default for every session if the `wallet_enabled` parameter is skipped. ```typescript theme={null} const { session } = await toffee.sessions.create({ // ...other params walletEnabled: true, }); ``` ```python theme={null} resp = await toffee.sessions.create(CreateSessionRequest( # ...other params wallet_enabled=True, )) ``` ```go theme={null} resp, err := toffee.Sessions.Create(ctx, &sdk.CreateSessionRequest{ // ...other params WalletEnabled: true, }) ``` ```http theme={null} POST /v1/sessions Authorization: Bearer Content-Type: application/json { ...other_params, "wallet_enabled": true } ``` ## Accounts Each user who activates Toffee Wallet gets an account linked to their game user(s). An account holds the user's balance, split into `real` (deposited funds) and `bonus` (cashback rewards). ### Get Account [Retrieve account details](/api-reference/accounts/get) by ID: ```typescript theme={null} const { account } = await toffee.accounts.get({ id: "acc_xyz123" }); ``` ```python theme={null} resp = await toffee.accounts.get(GetAccountRequest(id="acc_xyz123")) account = resp.account ``` ```go theme={null} resp, err := toffee.Accounts.Get(ctx, &sdk.GetAccountRequest{Id: "acc_xyz123"}) account := resp.GetAccount() ``` ```http theme={null} GET /v1/accounts/acc_xyz123 Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "acc_xyz123", "users": [ { "game_id": "space_shooter", "user_id": "player_42" } ], "currency": "USD", "balance": { "real": 2499, "bonus": 500 } } ``` ### List Accounts [Retrieve accounts](/api-reference/accounts/list) for a game: ```typescript theme={null} const { accounts } = await toffee.accounts.list({ gameId: "space_shooter" }); ``` ```python theme={null} resp = await toffee.accounts.list(ListAccountsRequest(game_id="space_shooter")) accounts = resp.accounts ``` ```go theme={null} resp, err := toffee.Accounts.List(ctx, &sdk.ListAccountsRequest{ GameId: "space_shooter", }) accounts := resp.GetAccounts() ``` ```http theme={null} GET /v1/accounts?game_id=space_shooter&limit=100&offset=0 Authorization: Bearer ``` **Response:** ```json theme={null} [ { "id": "acc_xyz123", "users": [ { "game_id": "space_shooter", "user_id": "player_42" } ], "currency": "USD", "balance": { "real": 2499, "bonus": 500 } } ] ``` *** ## Deposits Deposits represent top-up transactions into a user's Toffee Wallet. When a user adds funds through the Toffee Wallet app, a deposit is created and processed through the configured payment method. ### Deposit Statuses Deposits can have the following statuses: * `processing`: Deposit is being processed by the payment provider * `authorized`: Deposit has been authorized but not yet captured * `succeeded`: Deposit completed successfully * `cancelled`: Authorized deposit was cancelled before capture * `failed`: Deposit processing failed * `refunded`: Deposit was refunded ### Timestamp Fields Deposits include relevant timestamp fields based on their final status: * `created_at`: When the deposit was initiated * `authorized_at`: When the deposit was authorized (if applicable) * `succeeded_at`: When the deposit completed successfully (if applicable) * `failed_at`: When the deposit failed (if applicable) * `cancelled_at`: When the deposit was cancelled (if applicable) * `refunded_at`: When the deposit was refunded (if applicable) ### Webhook Events * [`deposit.succeeded`](/api-reference/webhooks/deposit.succeeded): When a deposit completes successfully * [`deposit.failed`](/api-reference/webhooks/deposit.failed): When a deposit attempt fails When a deposit is completed or failed, ToffeePay sends a **signed webhook** to your backend: ```json theme={null} { "type": "deposit.succeeded", "timestamp": "2023-06-01T12:10:00Z", "data": { // Deposit object } } ``` See the [Webhooks](/webhooks) page for implementation details and signature verification. ### Get Deposit [Retrieve details](/api-reference/deposits/get) of a deposit: ```typescript theme={null} const { deposit } = await toffee.deposits.get({ id: "dep_xyz789" }); ``` ```python theme={null} resp = await toffee.deposits.get(GetDepositRequest(id="dep_xyz789")) deposit = resp.deposit ``` ```go theme={null} resp, err := toffee.Deposits.Get(ctx, &sdk.GetDepositRequest{Id: "dep_xyz789"}) deposit := resp.GetDeposit() ``` ```http theme={null} GET /v1/deposits/dep_xyz789 Authorization: Bearer ``` **Response:** ```json theme={null} { "id": "dep_xyz789", "account_id": "acc_xyz123", "status": "succeeded", "amount": 2499, "currency": "USD", "method": "apple_pay", "details": "Top-up", "created_at": "2023-06-01T12:05:00Z", "succeeded_at": "2023-06-01T12:05:30Z" } ``` ### List Deposits [Retrieve historical deposits](/api-reference/deposits/list): ```typescript theme={null} const { deposits } = await toffee.deposits.list({ accountId: "acc_xyz123" }); ``` ```python theme={null} resp = await toffee.deposits.list(ListDepositsRequest(account_id="acc_xyz123")) deposits = resp.deposits ``` ```go theme={null} resp, err := toffee.Deposits.List(ctx, &sdk.ListDepositsRequest{ AccountId: "acc_xyz123", }) deposits := resp.GetDeposits() ``` ```http theme={null} GET /v1/deposits?account_id=acc_xyz123&limit=100&offset=0 Authorization: Bearer ``` **Response:** ```json theme={null} [ { "id": "dep_xyz789", "account_id": "acc_xyz123", "status": "succeeded", "amount": 2499, "currency": "USD", "method": "apple_pay", "details": "Top-up", "created_at": "2023-06-01T12:05:00Z", "succeeded_at": "2023-06-01T12:05:30Z" } ] ``` # Webhooks Source: https://docs.toffeepay.com/webhooks Event notifications and signature verification ToffeePay uses webhooks to notify your backend when important events occur, such as successful payments, completed refunds or successful wallet deposits. All webhooks are signed using [Standard Webhooks](https://www.standardwebhooks.com/). ## Webhook Structure All ToffeePay webhooks follow a consistent structure: ```json theme={null} { "type": "session.created", "timestamp": "2023-06-01T12:05:00Z", "data": { // Event-specific data } } ``` **Fields:** * **`type`**: Event type (e.g., `session.created`, `payment.succeeded`, `deposit.succeeded`) * **`timestamp`**: ISO 8601 timestamp when the event occurred * **`data`**: Event-specific payload containing the relevant entity data *** ## Webhook Events ### Payment Events | Event | Description | | ------------------------------------------------------------------ | --------------------------------------------- | | [`session.created`](/api-reference/webhooks/session.created) | A payment session was created | | [`session.paid`](/api-reference/webhooks/session.paid) | A session was successfully paid | | [`session.failed`](/api-reference/webhooks/session.failed) | A payment attempt related to a session failed | | [`session.expired`](/api-reference/webhooks/session.expired) | A session expired without payment | | [`session.cancelled`](/api-reference/webhooks/session.cancelled) | A session was cancelled | | [`payment.created`](/api-reference/webhooks/payment.created) | A payment was created | | [`payment.succeeded`](/api-reference/webhooks/payment.succeeded) | A payment was successfully processed | | [`payment.failed`](/api-reference/webhooks/payment.failed) | A payment attempt failed | | [`payment.authorized`](/api-reference/webhooks/payment.authorized) | A payment was authorized for later capture | | [`payment.cancelled`](/api-reference/webhooks/payment.cancelled) | A payment was cancelled | | [`refund.created`](/api-reference/webhooks/refund.created) | A refund was created | | [`refund.succeeded`](/api-reference/webhooks/refund.succeeded) | A refund was processed successfully | | [`refund.failed`](/api-reference/webhooks/refund.failed) | A refund attempt failed | ### Wallet Events | Event | Description | | ---------------------------------------------------------------- | --------------------------------------- | | [`deposit.succeeded`](/api-reference/webhooks/deposit.succeeded) | A wallet deposit completed successfully | | [`deposit.failed`](/api-reference/webhooks/deposit.failed) | A wallet deposit failed | *** ## Webhook Signature Verification All webhooks are signed using [Standard Webhooks](https://www.standardwebhooks.com/). Each request includes these headers: | Header | Description | | ------------------- | --------------------------------------------------- | | `webhook-id` | Unique message identifier for idempotency | | `webhook-timestamp` | Unix timestamp of the event | | `webhook-signature` | Signature in format `v1,{base64-encoded-signature}` | ### Verification Use the official [Standard Webhooks libraries](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries) to verify webhook signatures: ```typescript theme={null} import { Webhook } from "standardwebhooks"; const wh = new Webhook(process.env.WEBHOOK_SECRET); function handleWebhook(req: Request) { const payload = wh.verify(req.body, req.headers); switch (payload.type) { case 'session.paid': handleSessionPaid(payload.data); break; case 'payment.succeeded': handlePaymentSuccess(payload.data); break; case 'refund.succeeded': handleRefundSuccess(payload.data); break; case 'deposit.succeeded': handleDepositSuccess(payload.data); break; } } ``` *** ## Idempotency Webhooks may be delivered more than once. Use the `webhook-id` header to deduplicate: ```typescript theme={null} const webhookId = req.headers['webhook-id']; if (await isProcessed(webhookId)) { return; // Skip duplicate } // ... verify and process await markProcessed(webhookId); ``` *** ## Webhook Configuration You can configure and manage your webhook settings: 1. **Webhook URL**: The endpoint where ToffeePay will send webhooks 2. **Webhook Secret**: Used to sign webhook payloads (can be rotated) 3. **Event Selection**: Choose which events to receive (payments, refunds, deposits, or all) *** ## Best Practices 1. **Always verify signatures**: Never process webhooks without signature verification 2. **Handle duplicate events**: While rare, webhooks may be sent multiple times — use the `webhook-id` header for [idempotency](#idempotency) 3. **Respond quickly**: Return a 200 status code within 10 seconds to acknowledge receipt 4. **Retry logic**: ToffeePay will retry failed webhooks, but implement your own retry logic for critical operations 5. **Log webhook events**: Keep audit logs of all webhook events for debugging and compliance 6. **Use HTTPS**: Always use HTTPS endpoints for webhook URLs *** ## Troubleshooting ### Common Issues * **Signature verification fails**: Check that you're using the correct webhook secret and following the verification steps exactly * **Timeout errors**: Ensure your webhook endpoint responds within 10 seconds * **Missing webhooks**: Check your webhook URL configuration and ensure your endpoint is accessible ### Testing Webhooks Use tools like ngrok for local development to expose your local webhook endpoint to ToffeePay: ```bash theme={null} ngrok http 3000 ```