Trustly

DocsCurrentLast updated: February 13th 2024, @ 10:53:41 am


Trustly logo

With Trustly, buyers can initiate payments directly from their bank accounts. During the online checkout process, customers select their bank, log into their online account, choose their payment method, and verify their purchase.

Trustly integrates with banks to collect funds locally across Europe and offers real-time reconciliation through proprietary integrations. Trustly natively supports payments on merchant checkout and is optimized for most devices. Merchants receive credit notifications after payments are completed. Payment completion happens within 7 days of payment authorization, depending on the bank used for the payment.

CountriesPayment typePayment flowCurrenciesMinimum amountRefunds
Austria (AT)
Germany (DE)
Denmark (DK)
Estonia (EE)
Spain (ES)
Finland (FI)
Great Britain (GB)
Lithuania (LT)
Latvia (LV)
Netherlands (NL)
Norway (NO)
Sweden (SE)
bank redirectredirectEUR, DKK, SEK, GBP, NOK0.01 EUR(or equivalent)Up to 365 days

How it works

Checkout flow

Alternative payment methods diagram

  1. Your checkout page offers alternative payment methods.
  2. Buyer provides their personal details and selects an alternative payment method from your checkout page.
  3. Buyer is transferred from your checkout page to the third-party bank to confirm the purchase.
  4. Merchant receives the successful payment initiation webhook notification and the payment is in a pending state.
  5. Buyer authorizes and confirms payment.
  6. Buyer returns to your site.
  7. Merchant receives the successful payment completion webhook notification and PayPal moves the funds to the merchant account.
  8. Merchant ships the goods.

Eligibility

  • Available to merchants globally, except in Russia, Brazil, Belgium, Czechia, Poland, Slovakia, and Slovenia.
  • Billing agreements, multiple seller payments, and shipping callback aren't supported.
  • Only supports order capture. Doesn't support order authorization. See Payments webhooks.
  • Chargebacks aren't supported.
  • Payment must be an online purchase. Doesn't support in-store payments.

Know before you code

  • The steps to implementing all alternative payment methods using the Orders V2 REST API are similar. If you've implemented an alternative payment method previously, you can likely use that code and adjust it for the specific differences for Trustly.

  • Make sure you're subscribed to the following webhook events:

    • PAYMENT.CAPTURE.PENDING – Listen for this webhook as an indication that payment initialization was successful, the payment is in a pending state, and is waiting for the buyer to complete the payment. You'll act on this webhook in Step 3.
    • PAYMENT.CAPTURE.COMPLETED – Listen for this webhook as an indication that the buyer has completed the payment, which can take up to seven days. You can ship the order to the buyer at this point. You'll act on this webhook in Step 4.
    • PAYMENT.CAPTURE.DENIED - Listen for this webhook as an indication that the payer didn't complete the payment on time or the bank has declined the payment. You can cancel the order at this point. You'll act on this webhook in Step 4.

    Upon receiving each webhook, fetch the latest order details using Show order details. The up HATEOAS link in the webhook payload indicates the order associated with the capture.

  • Set your preference for receiving payments in your PayPal business or merchant account to accept and convert to the currency in your account. In your profile, select Account Settings > Payment preferences > Block payments and choose Update to mark this preference.

  • When processing Trustly payments, you don't need a call to capture payment for the order.

1. Offer Trustly on your checkout page

You'll need to create the user interface to offer Trustly and collect the buyer's information, then you'll use the API calls described in the remainder of this topic to:

  • Create the order with trustly as the payment source and the buyer's full_name and country_code.
  • redirect the buyer to Trustly

Refer to Payment method icons for icons you can use and download locations.

2. Create an order with Trustly as the payment source

Use the buyer information you captured from your user interface to create an order with Trustly as the payment source.

API endpoint used: Create order

  1. Sample request
  2. Sample response
1curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders' \
2-H 'Content-Type: application/json' \
3-H 'Authorization: Bearer ACCESS-TOKEN' \
4-H 'PayPal-Request-Id: PAYPAL-REQUESTID' \
5-d '{
6 "intent": "CAPTURE",
7 "purchase_units": [
8 {
9 "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
10 "amount": {
11 "currency_code": "EUR",
12 "value": "100.00"
13 }
14 }
15 ],
16 "payment_source": {
17 "trustly": {
18 "country_code": "NL",
19 "name": "Firstname Lastname"
20 }
21 },
22 "processing_instruction": "ORDER_COMPLETE_ON_PAYMENT_APPROVAL",
23 "application_context": {
24 "locale": "en-NL",
25 "return_url": "https://example.com/returnUrl",
26 "cancel_url": "https://example.com/cancelUrl"
27 }
28}'

Modify the code

After you copy the code in the sample request, modify the following:

  • ACCESS-TOKEN - Your access token.
  • PayPal-Request-Id - Replace the sample ID with a unique ID you generate. This ID helps prevent duplicate authorizations in the event that the API call is disrupted. See also: API idempotency.
  • intent - Set this value to CAPTURE.
  • payment_source - Specify trustly as the payment method, and include the following:
  • application_context - Specify the following:
    • locale - The preferred language for returned errors.
    • return_url - The URL the buyer is returned to after approving or canceling the purchase with their selected payment method.
    • cancel_url - The URL is a placeholder for future reference for now.
  • processing_instruction - Set this value to ORDER_COMPLETE_ON_PAYMENT_APPROVAL.
  • purchase_units: amount - Pass the value of the order and the currency code.

Note: Change or add other parameters in the Create order request body to create an order that reflects the actual order details.

Step result

A successful request results in the following:

  • A return status code of HTTP 200 OK.
  • A JSON response body that contains the order ID. You'll use the order ID and payer-action HATEOAS URL in the next step, Redirect buyer to Trustly. See also: HATEOAS links.

3. Redirect buyer to Trustly

In your user interface, attach the payer-action redirect URL returned in the Create order response to the Trustly payment button.

In the sample, the redirect URL is: "https://www.sandbox.paypal.com/payment/trustly?token=5O190127TN364715T".

Step result

  • The order status changes to COMPLETED, which means the order was created successfully.
  • A capture with PENDING status is present in the response parameter purchase_units[0].payments.captures[0]. The up HATEOAS link indicates the order associated with this capture.
  • The PAYMENT.CAPTURE.PENDING webhook event is triggered, which indicates that payment initiation was successful, the payment is in a pending state, and is waiting for the buyer to complete the payment.
  • This sends the buyer to their bank to approve the purchase.
  • Wait for the buyer to authorize the payment. Payment completion happens within 7 days of the payment authorization.

Sample PAYMENT.CAPTURE.PENDING webhook

1{
2 "id": "WH-4TV11484PJ0099250-1WB8208433136945G",
3 "event_version": "1.0",
4 "create_time": "2023-10-19T11:50:35.619Z",
5 "resource_type": "capture",
6 "resource_version": "2.0",
7 "event_type": "PAYMENT.CAPTURE.PENDING",
8 "summary": "Payment pending for EUR 15.39 EUR",
9 "resource": {
10 "id": "892032536L382192T",
11 "amount": {
12 "currency_code": "EUR",
13 "value": "15.39"
14 },
15 "final_capture": true,
16 "invoice_id": "Invoice-12345",
17 "custom_id": "Custom-1234",
18 "status": "PENDING",
19 "status_details": {
20 "reason": "OTHER"
21 },
22 "supplementary_data": {
23 "related_ids": {
24 "order_id": "5O190127TN364715T"
25 }
26 },
27 "payee": {
28 "email_address": "payee@example.com",
29 "merchant_id": "1111111111111"
30 },
31 "create_time": "2023-10-19T11:50:31Z",
32 "update_time": "2023-10-19T11:50:31Z",
33 "links": [
34 {
35 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/892032536L382192T",
36 "rel": "self",
37 "method": "GET"
38 },
39 {
40 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/892032536L382192T/refund",
41 "rel": "refund",
42 "method": "POST"
43 },
44 {
45 "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
46 "rel": "up",
47 "method": "GET"
48 }
49 ]
50 },
51 "links": [
52 {
53 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4TV11484PJ0099250-1WB8208433136945G",
54 "rel": "self",
55 "method": "GET"
56 },
57 {
58 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4TV11484PJ0099250-1WB8208433136945G/resend",
59 "rel": "resend",
60 "method": "POST"
61 }
62 ]
63}

4. Fulfill the order

Once the buyer authorizes the payment, and the payment completes:

  • The buyer is redirected to the return_url mentioned in the Create order request.
  • The capture status changes to COMPLETED.
  • The PAYMENT.CAPTURE.COMPLETED webhook event is triggered, which indicates that payment capture was successful. You can ship the order to the buyer at this point.

Note: Once the buyer authorizes the payment, payment completion happens within 7 days, depending on the bank used for the payment. You should wait until payment completion to ship the goods.

If the payment is not successful:

  • The buyer is redirected to the return_url mentioned in the Create order request.
  • The capture status changes to DENIED.
  • The PAYMENT.CAPTURE.DENIED webhook event is triggered, which indicates that the payment capture wasn't successful. You shouldn't fulfill the order.

See Subscribe to checkout webhooks for more information.

Here are some additional resources as you create webhook handler code:

Sample PAYMENT.CAPTURE.COMPLETED webhook

1{
2 "id": "WH-81H706078A5332206-4WN94402WA949352E",
3 "event_version": "1.0",
4 "zts": 1481046241,
5 "create_time": "2023-10-19T12:18:27.538Z",
6 "resource_type": "capture",
7 "resource_version": "2.0",
8 "event_type": "PAYMENT.CAPTURE.COMPLETED",
9 "summary": "Payment completed for EUR 15.39 EUR",
10 "resource": {
11 "id": "892032536L382192T",
12 "amount": {
13 "currency_code": "EUR",
14 "value": "15.39"
15 },
16 "final_capture": true,
17 "status": "COMPLETED",
18 "status_details": {
19 "reason": "OTHER"
20 },
21 "payee": {
22 "email_address": "payee@example.com",
23 "merchant_id": "1111111111111"
24 },
25 "create_time": "2023-10-19T11:50:31Z",
26 "update_time": "2023-10-19T12:18:22Z",
27 "links": [
28 {
29 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/892032536L382192T",
30 "rel": "self",
31 "method": "GET"
32 },
33 {
34 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/892032536L382192T/refund",
35 "rel": "refund",
36 "method": "POST"
37 },
38 {
39 "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
40 "rel": "up",
41 "method": "GET"
42 }
43 ]
44 },
45 "links": [
46 {
47 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-81H706078A5332206-4WN94402WA949352E",
48 "rel": "self",
49 "method": "GET"
50 },
51 {
52 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-81H706078A5332206-4WN94402WA949352E/resend",
53 "rel": "resend",
54 "method": "POST"
55 }
56 ]
57}

Sample PAYMENT.CAPTURE.DENIED webhook

1{
2 "id": "WH-1W932300CU741650D-26E32915YU381323H",
3 "event_version": "1.0",
4 "create_time": "2023-10-19T12:44:23.339Z",
5 "resource_type": "capture",
6 "resource_version": "2.0",
7 "event_type": "PAYMENT.CAPTURE.DENIED",
8 "summary": "Payment denied for EUR15.39 EUR",
9 "resource": {
10 "id": "55X2488039624323N",
11 "amount": {
12 "currency_code": "EUR",
13 "value": "15.39"
14 },
15 "final_capture": true,
16 "invoice_id": "Invoice-12345",
17 "custom_id": "Custom-1234",
18 "status": "DECLINED",
19 "status_details": {
20 "reason": "OTHER"
21 },
22 "supplementary_data": {
23 "related_ids": {
24 "order_id": "9R480625CB7539041"
25 }
26 },
27 "payee": {
28 "email_address": "payee@example.com",
29 "merchant_id": "1111111111111"
30 },
31 "create_time": "2023-10-19T12:43:41Z",
32 "update_time": "2023-10-19T12:44:18Z",
33 "links": [
34 {
35 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/55X2488039624323N",
36 "rel": "self",
37 "method": "GET"
38 },
39 {
40 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/55X2488039624323N/refund",
41 "rel": "refund",
42 "method": "POST"
43 },
44 {
45 "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/9R480625CB7539041",
46 "rel": "up",
47 "method": "GET"
48 }
49 ]
50 },
51 "links": [
52 {
53 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-1W932300CU741650D-26E32915YU381323H",
54 "rel": "self",
55 "method": "GET"
56 },
57 {
58 "href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-1W932300CU741650D-26E32915YU381323H/resend",
59 "rel": "resend",
60 "method": "POST"
61 }
62 ]
63}

Note: The order ID from Step 2 should match the resource.supplementary_data.related_ids.order_id parameter in the webhook payload.

Alternatively, you can get the order capture result from the Show order details endpoint.

Important: Exercise caution when polling for order capture result using the Show order details endpoint. PayPal enforces rate limits on API requests.

  1. Sample request
  2. Sample response
1curl -v -X GET https://api-m.sandbox.paypal.com/v2/checkout/orders/9R480625CB7539041 \
2-H "Content-Type: application/json" \
3-H "Authorization: Bearer ACCESS-TOKEN"

Step result

A successful request returns the HTTP 200 OK status code with a JSON response body that returns a COMPLETED status.

A successfully captured order has the following:

  • The order status as COMPLETED, which means the order was captured successfully.
  • A capture with COMPLETED status is present in the response parameter purchase_units[0].payments.captures[0].
  • The up HATEOAS link indicates the order associated with this capture.

5. Handle payment responses

This section shows how to handle payment responses.

Successful payment

After a successful payment, notify the buyer of a successful transaction. You can do this by sending a confirmation email.

Unsuccessful payment

If Step 2 is unsuccessful and returns an HTTP 422 UNPROCESSABLE_ENTITY status code, the JSON response body should contain an error code in the issue parameter. Use this information to display the appropriate error message to the buyer.

API endpoint used: Create order

  1. Sample request
  2. Sample response
1curl -L -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders' \
2-H 'Content-Type: application/json' \
3-H 'Authorization: Bearer ACCESS-TOKEN' \
4-H 'PayPal-Request-Id: PAYPAL-REQUESTID' \
5--data-raw '{
6 "intent": "CAPTURE",
7 "purchase_units": [
8 {
9 "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
10 "amount": {
11 "currency_code": "SEK",
12 "value": "100.00"
13 }
14 }
15 ],
16 "payment_source": {
17 "trustly": {
18 "country_code": "NL",
19 "name": "Firstname Lastname"
20 }
21 },
22 "processing_instruction": "ORDER_COMPLETE_ON_PAYMENT_APPROVAL",
23 "application_context": {
24 "locale": "en-NL",
25 "return_url": "https://example.com/returnUrl",
26 "cancel_url": "https://example.com/cancelUrl"
27 }
28}'

Step result

An unsuccessful request returns a JSON response body that includes:

  • A return status code of 422 Unprocessable Entity.
  • An error code in the details.issue parameter.
  • An error description in the details.description parameter.

Next steps