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 endpoint | Returns | Used by |
|---|---|---|
| Create a payment | { id, objectSecret } of a new Payment Order | authorizePaymentOrder() |
| Connection token | { secret, locationId, onBehalfOf } | tokenProvider in init() |
Protect both endpointsOnly 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
| Call | Made by | Authentication |
|---|---|---|
POST /auth/authentication/{shopId}/secret-key | your backend | secret key, returns a JWT valid 10 minutes |
POST /payment/payment-orders | your backend | JWT |
POST /account/wallets/{walletId}/locations/connection_tokens | your backend | JWT |
POST /payment/payment-orders/{id}/tap-to-pay | the SDK | public key + object_secret |
| Environment | Base URL |
|---|---|
| Sandbox | https://sb-api.paygreen.fr |
| Production | https://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
pendingand 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"| Field | Description |
|---|---|
secret | The connection token. Required. |
locationId | Tap to Pay location of the shop (tml_…). Required to connect. |
onBehalfOf | Connected 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.
Updated 11 days ago