Skip to main content

Checkout

This page details how the checkout flow works. It allows you to make a payer pay for a payee's item and initiate a transaction between the two. It assumes that you already have credentials to successfully call Safepay Nordic APIs and have subscribed to webhooks.

Both the payer and the payee must be connected with Safepay Nordic, so you can identify them by their safepayAccountId in payerId and payeeId.

How it works​

  • When a user wants to buy an item on your service, your backend creates a transaction.
  • Your backend creates a payment for the transaction.
  • Your backend chooses the payment method, preferably one of the payer's bank accounts, and gets a paymentUrl.
  • You redirect the payer to the paymentUrl.
  • The payer pays the amount into Safepay Nordic's escrow account and is sent back to your returnUrl.
  • Your backend receives a transaction.payment.paid webhook.

If the payer can't pay with PSD2, they can pay by bank transfer instead.

Create transaction​

Create transaction
curl https://sandbox-api.safepaynordic.dk/v2/transactions/marketplace \
-X POST \
-H 'Authorization: Bearer {API_TOKEN}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 81784a78-8b7f-4e49-ad4e-383417c970cf' \
-d '{
"title": "Adidas Campus 00s",
"description": "Almost new Adidas Campus 00s. Used 3 times and still have the box and receipt.",
"amount": 1200.50,
"currency": "DKK",
"payeeId": "82f700bd-5789-4623-95e7-0d34fde4720c",
"payerId": "003dc3d2-dc30-4b37-a573-f664a03f5154",
"payeeFixedFee": 15,
"payerFixedFee": 10,
"referenceId": "your-reference-id",
"imageUrls": [ "https://domain.com/public-image-url-1.jpg" ]
}'
Response example
{
"transactionId": "295a2546-ee78-4ec4-9e0f-b49af13536b8"
}

Use the transactionId to create a payment.

Parameters​

  • title: Required. Shown to the parties during checkout.
  • amount: Required. Rounded to 2 decimals. It must cover the payee's fees.
  • payerId: Required. The payer's safepayAccountId.
  • payeeId: Required. The payee's safepayAccountId.
  • currency: Optional. DKK (default) or EUR.
  • description: Optional. Free-text description of the transaction.
  • imageUrls: Optional. Public URLs of images of the item.
  • payeeFixedFee / payerFixedFee: Optional. Your fee, charged to the payee and the payer respectively. Together they make up your fee for the transaction, which Safepay Nordic's fee is taken from. If your fee doesn't cover Safepay Nordic's fee, the difference is billed to you.
  • referenceId: Optional. Your own reference for the transaction. It's returned on the transaction and in webhooks, and can be used to filter when listing transactions.
  • expectedCompletion: Optional. When the transaction is expected to complete. Must be in the future.
  • documentId: Optional. Connects an existing document to the transaction.

Retrying safely​

Send an Idempotency-Key header, as in the example above, so a retried request can't create a second transaction. See Idempotency.

Errors​

All errors are returned as 400 Bad Request with one of these titles:

  • Invalid payee: payeeId is missing.
  • Invalid amount: the amount doesn't cover the fees.
  • Invalid document: the documentId wasn't found.

Create payment​

Create a payment for the transaction.

Create payment
curl https://sandbox-api.safepaynordic.dk/v2/payments \
-X POST \
-H 'Authorization: Bearer {API_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"transactionId": "295a2546-ee78-4ec4-9e0f-b49af13536b8",
"safepayAccountId": "003dc3d2-dc30-4b37-a573-f664a03f5154",
"returnUrl": "https://marketplace.dk/orders/1234"
}'
Response example
{
"paymentId": "7c1e9a3d-2f4b-4d8e-a6c5-0b9f8e7d6a51",
"paymentUrl": "https://sandbox.mitsafepay.dk/l/p7c1e9a3d"
}

Next, choose the payment method to get the paymentUrl to send the payer to. The paymentUrl returned here shows a screen where the payer chooses how to pay; only use it if you can't choose the payment method yourself.

Each call creates a new payment, so send an Idempotency-Key header to retry safely, as described in Idempotency.

Parameters​

  • transactionId: Required. The transaction to pay for.
  • safepayAccountId: Required. The safepayAccountId of the user who pays, usually the transaction's payer. Must be connected to you.
  • amount: Optional. Defaults to the remaining amount to pay, including the payer's fees. Must be greater than zero and can't exceed the remaining amount.
  • description: Optional. Defaults to the transaction's Safepay ID.
  • returnUrl: Optional. Where the payer is sent after the payment.
  • skipUI: Optional. When true, the payer is redirected straight to your returnUrl after the payment, without seeing the Safepay Nordic status screen. Only applies when returnUrl is set.

Errors​

  • 400 Invalid amount: the amount is zero or negative, or higher than the remaining amount.
  • 404 Not Found: the transaction wasn't found, or the safepayAccountId doesn't exist or isn't connected to you.

Choose payment method​

Choose how the payer pays and get the paymentUrl to send them to. We recommend PSD2 with one of the payer's bank accounts, so the payer goes straight to approving the payment in their bank.

Initialize payment
curl https://sandbox-api.safepaynordic.dk/v2/payments/{paymentId}/initialize \
-X POST \
-H 'Authorization: Bearer {API_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"paymentMethod": "PSD2",
"bankAccountId": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}'
Response example
{
"paymentUrl": "https://sandbox.mitsafepay.dk/l/p7c1e9a3d/psd2/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}
  • paymentMethod: One of:
    • PSD2 (recommended): pay from one of the payer's bank accounts, given by bankAccountId from List bank accounts.
    • BANK: pay from an account in the bank given by bankId, a bankId from List banks.
    • VIPPSMOBILEPAY: pay with Vipps.
  • bankAccountId: Required for PSD2. Must be one of the paying user's own bank accounts.
  • bankId: Required for BANK.

If paymentMethod is omitted, the returned paymentUrl shows the payment method selection screen, as with the paymentUrl from Create payment. A missing bankAccountId or a missing or invalid bankId returns 400. A bankAccountId that isn't one of the paying user's bank accounts returns 404 Bank account not found, and an unknown paymentId returns 404.

Pay by bank transfer​

If the payer can't pay with PSD2, they can pay with an ordinary bank transfer into Safepay Nordic's escrow account. Get the details to show the payer:

Get payment instructions
curl https://sandbox-api.safepaynordic.dk/v2/transactions/{transactionId}/payment-instructions/{safepayAccountId} \
-H 'Authorization: Bearer {API_TOKEN}'
Response example
{
"amount": 1210.50,
"currency": "DKK",
"bankMessage": "DT-1A2B3C4D",
"bban": "{ESCROW_BBAN}",
"iban": "{ESCROW_IBAN}",
"swift": "JYBADKKK",
"fik": "{FIK_CODE}"
}
  • transactionId: The transaction to pay for.
  • safepayAccountId: The safepayAccountId of the user who pays, usually the transaction's payer. Must be connected to you. Money paid this way is attributed to this user, and any refund is paid back to them.

You don't need to create a payment first.

Response​

  • amount: What's left to pay, including the payer's fees, minus what has already been paid. Ask again after a partial payment to get the new amount.
  • currency: DKK or EUR, the transaction's currency.
  • fik: A FIK code (Fælles indbetalingskort, also called FI-kort) for the transfer. Only card type +71 is supported. Show the code to the payer exactly as returned. Empty for EUR.
  • bban / iban / swift: Safepay Nordic's escrow account, for a transfer to an account number.
  • bankMessage: The reference the payer must put in the transfer's message when paying to the account number.

How the transfer is matched​

  • FIK (recommended for DKK): the transfer is matched automatically when it shows up on Safepay Nordic's bank account, and you receive the transaction.payment.paid webhook.
  • Account number with bankMessage (always the case for EUR): the transfer is matched by Safepay Nordic, and you receive the transaction.payment.paid webhook once it's matched. A transfer without the bankMessage or with the wrong one can't be matched to the transaction, which delays it.

Transfers older than 30 days aren't matched automatically and are handled by Safepay Nordic.

A bank transfer doesn't confirm the payment while the payer is on your site. Show the payer the instructions, and wait for the webhook before you treat the transaction as paid.

Errors​

  • 404 Not Found: the transaction wasn't found, or the safepayAccountId doesn't exist or isn't connected to you.

After payment​

When the payment is done, the payer is sent to your returnUrl: directly if you set skipUI, otherwise via a Safepay Nordic status screen with a button back to you. If you didn't set a returnUrl, the payer stays on the status screen.

No payment status is added to the returnUrl, and the payer is also sent there if the payment failed. Confirm the state server-side using the webhooks below or by calling GET /v2/transactions/{transactionId} (see Transaction details).

Webhooks​

Safepay Nordic sends a webhook when the transaction changes.

Webhook example
{
"eventName": "transaction.payment.paid",
"eventDate": "2026-01-01T12:00:00+01:00",
"webhookId": "ed3325d8-b379-444a-9cc1-7d119efefbea",
"transactionId": "295a2546-ee78-4ec4-9e0f-b49af13536b8",
"paymentId": "7c1e9a3d-2f4b-4d8e-a6c5-0b9f8e7d6a51",
"referenceId": "your-reference-id",
"payee": {
"safepayAccountId": "82f700bd-5789-4623-95e7-0d34fde4720c",
"referenceId": "your-payee-reference-id"
},
"payer": {
"safepayAccountId": "003dc3d2-dc30-4b37-a573-f664a03f5154",
"referenceId": "your-payer-reference-id"
}
}

paymentId is only set for the payment events.

eventNameSent when
transaction.payment.paidA payment from the payer into escrow has been authorized.
transaction.payment.failedA payment from the payer failed.
transaction.succeededThe transaction completed and the money is released to the payee.
transaction.cancelledThe transaction was cancelled.

Next steps​

When the trade is completed or falls through, release the payout or cancel the transaction as described in Complete transaction. Use Transaction details to read the transaction.