payout / developers
Guide

Recurrent payment

On this page

Save the customer's card once and charge it later without the customer, for example for a subscription.

Before you begin

  • Recurrent payments must be enabled for your account; contact support.
  • An API key and a Bearer token, as in Simple payment, steps 1 and 2.

Steps

  1. Create a checkout that stores the card

    Create the checkout as in Simple payment, with "mode": "store_card" and "recurring": true, and redirect the customer to checkout_url from the response. "recurring": true marks the stored card for recurrent payments. Without it, the card issuer may ask for 3-D Secure on later payments, which fails because the customer isn't there to confirm it.

    Command Line
    curl --location --request POST 'https://sandbox.payout.one/api/v1/checkouts' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header 'Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU' \
    --header 'Idempotency-Key: 314261f7-75ea-4e69-bd19-8c790ceb76d3' \
    --data-raw '{
        "amount": 300,
        "currency": "EUR",
        "mode": "store_card",
        "recurring": true,
        "customer": {
            "first_name": "John",
            "last_name": "Doe",
            "email": "[email protected]"
        },
        "external_id": "order-3001",
        "nonce": "QU02YWNWazBKcnAxZWg2eg",
        "redirect_url": "https://eshop.example.com/payment/redirect",
        "signature": "e539cd250d16627408a3017904fac9e85e9141cfd3d4a621050740996f91a859"
    }'
    

    amount is in cents: 300 is 3.00 EUR. Sign the request as in Simple payment:

    TEXT
    Pattern: amount|currency|external_id|nonce|client_secret
    Input:   300|EUR|order-3001|QU02YWNWazBKcnAxZWg2eg|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C
    SHA-256: e539cd250d16627408a3017904fac9e85e9141cfd3d4a621050740996f91a859
    

    Note

    Signatures are SHA-256 hashes in lowercase hex. Some libraries return uppercase hex; convert it to lowercase before you send or compare it.

  2. Receive the recurrent token

    After a successful payment, Payout sends two webhooks: checkout.succeeded, as in Simple payment, and payu_token.created. The second one carries the masked card number in card_mask and the recurrent token in token_value. It also contains the card expiry in exp_month and exp_year.

    payu_token.created payload:

    JSON
    {
        "external_id": "order-3001",
        "object": "webhook",
        "type": "payu_token.created",
        "data": {
            "object": "payu_token",
            "checkout_id": 141601,
            "card_mask": "424575******9685",
            "token_value": "QTEyOEdDTQ.ZXhhbXBsZS1lbmNyeXB0ZWQta2V5.ZXhhbXBsZS1pdg.ZXhhbXBsZS1jYXJkLXRva2VuLW5vdC1yZWFs.ZXhhbXBsZS10YWc"
        },
        "nonce": "R1NHWGpSUzBzMFM4QXBVVQ",
        "signature": "e06940e780fcdf14039776f6f9e665c71d21ec1e38eda0b1a76b001126e187c2"
    }
    

    Verify the signature of both webhooks as in Simple payment. For this webhook:

    TEXT
    Pattern: external_id|type|nonce|client_secret
    Input:   order-3001|payu_token.created|R1NHWGpSUzBzMFM4QXBVVQ|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C
    SHA-256: e06940e780fcdf14039776f6f9e665c71d21ec1e38eda0b1a76b001126e187c2
    

    Save token_value with the customer's account.

  3. Charge the card

    For each recurrent payment, create a new checkout like the one in step 1, with a new Idempotency-Key header and these changes in the body:

    JSON
    {
        "mode": "recurrent",
        "recurrent_token": "QTEyOEdDTQ.ZXhhbXBsZS1lbmNyeXB0ZWQta2V5.ZXhhbXBsZS1pdg.ZXhhbXBsZS1jYXJkLXRva2VuLW5vdC1yZWFs.ZXhhbXBsZS10YWc",
        "external_id": "order-3002",
        "nonce": "ZVRVYW5uU3JQM3pCcXdRRQ",
        "signature": "a92120698baf7729dccce17922ab1f614d2411572c3e45a58ceb732cf570f00c"
    }
    

    recurrent_token is the token_value from the payu_token.created webhook. The signature is computed from the new values:

    TEXT
    Pattern: amount|currency|external_id|nonce|client_secret
    Input:   300|EUR|order-3002|ZVRVYW5uU3JQM3pCcXdRRQ|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C
    SHA-256: a92120698baf7729dccce17922ab1f614d2411572c3e45a58ceb732cf570f00c
    

    The customer doesn't take part: Payout charges the card in the background, so there is no redirect.

  4. Wait for the result

    After a successful payment, Payout sends checkout.succeeded; handle it as in Simple payment, steps 7 to 9. If the payment fails, Payout sends recurrent.failed.

Next steps

  • Try the flow in the sandbox with the test cards.
  • For payments that the customer confirms each time, see Store card.

Was this page helpful?