Backend integration

Create Tap to Pay Payment Orders and serve connection tokens from your backend.

Your backend does what the app must never do: it holds the secret key, creates Payment Orders and requests connection tokens. Expose two endpoints of your backend to the app:

Your endpointReturnsUsed by
Create a payment{ id, objectSecret } of a new Payment OrderauthorizePaymentOrder()
Connection token{ secret, locationId, onBehalfOf }tokenProvider in init()
❗️

Protect both endpoints

Only your authenticated users must reach them. Anyone who can call your connection token endpoint can open Tap to Pay sessions on your account.


PayGreen API calls

CallMade byAuthentication
POST /auth/authentication/{shopId}/secret-keyyour backendsecret key, returns a JWT valid 10 minutes
POST /payment/payment-ordersyour backendJWT
POST /account/wallets/{walletId}/locations/connection_tokensyour backendJWT
POST /payment/payment-orders/{id}/tap-to-paythe SDKpublic key + object_secret
EnvironmentBase URL
Sandboxhttps://sb-api.paygreen.fr
Productionhttps://api.paygreen.fr

The examples below use the sandbox. API, SHOP_ID, WALLET_ID and SECRET_KEY come from your configuration. The PHP examples use Guzzle with base_uri set to the API URL; the Python examples use Requests.


1. Get a JWT

Every call below uses a JWT obtained with your secret key. It is valid 10 minutes: cache it and renew it before it expires. See Authentication.

async function getJwt() {
  const response = await fetch(`${API}/auth/authentication/${SHOP_ID}/secret-key`, {
    method: 'POST',
    headers: { Authorization: SECRET_KEY },
  });
  if (!response.ok) throw new Error(`Authentication: ${response.status}`);
  const { data } = await response.json();
  return data.token;
}
function getJwt(Client $api): string
{
    $response = $api->post('/auth/authentication/' . SHOP_ID . '/secret-key', [
        'headers' => ['Authorization' => SECRET_KEY],
    ]);

    return json_decode($response->getBody(), true)['data']['token'];
}
def get_jwt() -> str:
    response = requests.post(
        f'{API}/auth/authentication/{SHOP_ID}/secret-key',
        headers={'Authorization': SECRET_KEY},
        timeout=15,
    )
    response.raise_for_status()
    return response.json()['data']['token']
curl -X POST "https://sb-api.paygreen.fr/auth/authentication/$SHOP_ID/secret-key" \
  -H "Authorization: $SECRET_KEY"

2. Create the Payment Order

Create one Payment Order per sale with Create a Payment Order, then return its id and object_secret to the app.

A Tap to Pay Payment Order must:

  • have source: tap_to_pay,
  • have auto_capture: true,
  • have no buyer,
  • still be pending and not expired when the app authorizes it.

The shop needs an enabled bank_card payment config.

async function createTapToPayOrder(amount) {
  const response = await fetch(`${API}/payment/payment-orders`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${await getJwt()}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      shop_id: SHOP_ID,
      amount, // in cents, decided by your backend
      currency: 'eur',
      source: 'tap_to_pay',
      auto_capture: true,
    }),
  });
  if (!response.ok) throw new Error(`Payment Order: ${response.status}`);
  const { data } = await response.json();
  return { id: data.id, objectSecret: data.object_secret };
}
function createTapToPayOrder(Client $api, int $amount): array
{
    $response = $api->post('/payment/payment-orders', [
        'headers' => ['Authorization' => 'Bearer ' . getJwt($api)],
        'json' => [
            'shop_id' => SHOP_ID,
            'amount' => $amount, // in cents, decided by your backend
            'currency' => 'eur',
            'source' => 'tap_to_pay',
            'auto_capture' => true,
        ],
    ]);
    $data = json_decode($response->getBody(), true)['data'];

    return ['id' => $data['id'], 'objectSecret' => $data['object_secret']];
}
def create_tap_to_pay_order(amount: int) -> dict:
    response = requests.post(
        f'{API}/payment/payment-orders',
        headers={'Authorization': f'Bearer {get_jwt()}'},
        json={
            'shop_id': SHOP_ID,
            'amount': amount,  # in cents, decided by your backend
            'currency': 'eur',
            'source': 'tap_to_pay',
            'auto_capture': True,
        },
        timeout=15,
    )
    response.raise_for_status()
    data = response.json()['data']
    return {'id': data['id'], 'objectSecret': data['object_secret']}
curl -X POST https://sb-api.paygreen.fr/payment/payment-orders \
  -H "Authorization: Bearer $JWT" \
  -H 'Content-Type: application/json' \
  -d '{"shop_id": "sh_xxx", "amount": 1000, "currency": "eur", "source": "tap_to_pay", "auto_capture": true}'

3. Serve the connection token

The SDK needs a new connection token each time it opens a Tap to Pay session. Request it with your JWT and return it to the app, with the location and the connected account:

async function createConnectionToken() {
  const response = await fetch(
    `${API}/account/wallets/${WALLET_ID}/locations/connection_tokens`,
    { method: 'POST', headers: { Authorization: `Bearer ${await getJwt()}` } }
  );
  if (!response.ok) throw new Error(`Connection token: ${response.status}`);
  const { data } = await response.json();
  return {
    secret: data.secret,
    locationId: data.location_id,
    onBehalfOf: data.on_behalf_of,
  };
}
function createConnectionToken(Client $api): array
{
    $response = $api->post('/account/wallets/' . WALLET_ID . '/locations/connection_tokens', [
        'headers' => ['Authorization' => 'Bearer ' . getJwt($api)],
    ]);
    $data = json_decode($response->getBody(), true)['data'];

    return [
        'secret' => $data['secret'],
        'locationId' => $data['location_id'],
        'onBehalfOf' => $data['on_behalf_of'],
    ];
}
def create_connection_token() -> dict:
    response = requests.post(
        f'{API}/account/wallets/{WALLET_ID}/locations/connection_tokens',
        headers={'Authorization': f'Bearer {get_jwt()}'},
        timeout=15,
    )
    response.raise_for_status()
    data = response.json()['data']
    return {
        'secret': data['secret'],
        'locationId': data['location_id'],
        'onBehalfOf': data['on_behalf_of'],
    }
curl -X POST "https://sb-api.paygreen.fr/account/wallets/$WALLET_ID/locations/connection_tokens" \
  -H "Authorization: Bearer $JWT"
FieldDescription
secretThe connection token. Required.
locationIdTap to Pay location of the shop (tml_…). Required to connect.
onBehalfOfConnected account (acct_…). Required on iPhone, ignored on Android.

If you serve several shops, use the wallet of the shop the app is signed in to.


Did this page help you?