On this page
No Headings
Last updated: June 25, 2026
Render payment buttons and process payments with the Orders API.
Note: The integration steps for implementing alternative payment methods are similar. If you've integrated another alternative payment method before, you can likely reuse that code with adjustments for this payment method.
PAYMENT.CAPTURE.COMPLETED - Listen for this webhook to get notified about a successful order capture.PAYMENT.CAPTURE.DENIED - This webhook tells you when an order capture fails.MERCHANT-COUNTRY in the URL with the 2-character country code for the merchant's country of operation:
https://www.sandbox.paypal.com/bizsignup/entry?product=ideal&capabilities=IDEAL&country.x=MERCHANT-COUNTRYhttps://www.paypal.com/bizsignup/entry?product=ideal&capabilities=IDEAL&country.x=MERCHANT-COUNTRYYou'll need to create the user interface to offer banks through iDEAL and collect the buyer's information, then you'll use the API calls described in the remainder of this topic to:
full_name, country_code and iDEAL as the payment source.Use the buyer information you captured from your user interface to create an order with iDEAL as the payment source.
API endpoint used in Sample request: Create order
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders' \
-H "PayPal-Request-Id: PAYPAL-REQUEST-ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{
"intent": "CAPTURE",
"purchase_units": [
{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"amount": {
"currency_code": "EUR",
"value": "1.00"
}
}
],
"payment_source": {
"ideal": {
"country_code": "NL",
"name": "Firstname Lastname"
}
}
}'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 idempotencyintent - To use an alternative payment method, this parameter must be set to CAPTURE as it is in this sample code.payment_source - Specify ideal as the payment method and include the country code and account holder's full name.purchase_units.amount - Pass the amount of the order and the currency code.processing_instruction - Set this value to ORDER_COMPLETE_ON_PAYMENT_APPROVAL as it is in this sample code.application_context - Specify the preferred language for returned errors, the URL the buyer is returned to after approving the purchase with their selected payment method, and the URL the buyer is returned to after canceling an approval with their selected payment method. While return_url and cancel_url are optional fields, this integration requires you specify them to handle the handoff from the payment method back to your site. You can use the cancel_url to redirect buyers when an error occurs while they're on the payment method's site, so make sure your cancel URL works for that situation as well as an actual cancellation by the buyer.Note: Change or add other parameters in the Create order request body to create an order that reflects the actual order details.
A successful request results in the following:
200 OK.payer-action HATEOAS URL in the next step. See also: HATEOAS links.Partners need to onboard merchants upfront before they accept payments. iDEAL doesn't support onboarding after making payments, specifically Progressive Onboarding. See the Onboard a merchant section for more details.
When you submit an order with ideal as the payment_source, and the merchant isn't onboarded, you get the following error message:
The 'API caller' and/or 'payee' is not set up to be able to process the selected payment source. If you have already completed the required steps, please allow 2 business days for PayPal to complete the setup. If you continue to receive this error, please contact your Account Manager or check status at https://www.paypal.com/businessmanage/account/payments.
This code sample shows an unsuccessful attempt to confirm an order without a properly onboarded merchant.
API endpoint used in the sample request: Create order
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER-ID/confirm-payment-source' \
-H "PayPal-Request-Id: PAYPAL-REQUEST-ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{
"payment_source": {
"ideal": {
"country_code": "NL",
"name": "Firstname Lastname",
"experience_context": {
"return_url": "https://example.com/return",
"cancel_url": "https://example.com/cancel"
}
}
}
}'This unsuccessful single-shot create order request results in the following:
422 Unprocessable Entity.NOT_ENABLED_FOR_PAYMENT_SOURCE, a description of the error, and a message that details.fields.field has an incorrect value.This code sample shows an unsuccessful attempt to create a new order and capture the payment without a properly onboarded merchant.
API endpoint used in the sample request: Create order
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders' \
-H "PayPal-Request-Id: PAYPAL-REQUEST-ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{
"intent": "CAPTURE",
"payment_source": {
"ideal": {
"country_code": "NL",
"name": "Firstname Lastname",
"experience_context": {
"return_url": "https://example.com/return",
"cancel_url": "https://example.com/cancel"
}
}
},
"processing_instruction": "ORDER_COMPLETE_ON_PAYMENT_APPROVAL",
"purchase_units": [
{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"amount": {
"currency_code": "EUR",
"value": "100"
}
}
]
}
}'This unsuccessful single-shot create order request results in the following:
422 Unprocessable Entity.NOT_ENABLED_FOR_PAYMENT_SOURCE, a description of the error, and a message that details.fields.field has an incorrect value.In your user interface, attach the payer-action redirect URL returned in the Create Order response to the iDEAL payment button. This sends the buyer to their bank to approve the purchase. Once the buyer approves the purchase, the payment is automatically captured.
In the sample, the redirect URL is: "https://www.sandbox.paypal.com/payment/ideal?token=5KP92830L1747245S".
After successfully completing the bank approval:
return_url mentioned in the Create Order request.PAYMENT.CAPTURE.COMPLETED webhook event is triggered, which indicates that payment capture was successful.After unsuccessful bank approval:
cancel_url mentioned in the Create Order request.Listen to the following webhooks to get the result of order capture:
PAYMENT.CAPTURE.COMPLETED webhook event indicates a successful order capture.PAYMENT.CAPTURE.DENIED webhook events indicate a failed order capture.up HATEOAS link indicates the order associated with this capture.See Subscribe to checkout webhooks for more information.
Here are some additional resources as you create webhook handler code:
{
"id": "WH-2B342482FC0449155-12X09416XP387753C",
"event_version": "1.0",
"zts": 1481046241,
"create_time": "2022-04-08T10:37:05Z",
"resource_type": "capture",
"resource_version": "2.0",
"event_type": "PAYMENT.CAPTURE.COMPLETED",
"summary": "Payment completed for EUR 1.00 EUR",
"resource": {
"amount": {
"value": "1.00",
"currency_code": "EUR"
},
"create_time": "2022-04-08T10:37:05Z",
"update_time": "2022-04-08T10:37:05Z",
"final_capture": true,
"seller_receivable_breakdown": {
"paypal_fee": {
"value": "0.20",
"currency_code": "EUR"
},
"gross_amount": {
"value": "1.00",
"currency_code": "EUR"
},
"net_amount": {
"value": "0.80",
"currency_code": "EUR"
}
},
"links": [
{
"method": "GET",
"rel": "self",
"href": "https://api-m.sandbox.paypal.com/v2/payments/captures/8SS60826HT082593F"
},
{
"method": "POST",
"rel": "refund",
"href": "https://api-m.sandbox.paypal.com/v2/payments/captures/8SS60826HT082593F/refund"
},
{
"method": "GET",
"rel": "up",
"href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5KP92830L1747245S"
}
],
"id": "8SS60826HT082593F",
"status": "COMPLETED"
},
"links": [
{
"href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2B342482FC0449155-12X09416XP387753C",
"rel": "self",
"method": "GET"
},
{
"href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2B342482FC0449155-12X09416XP387753C/resend",
"rel": "resend",
"method": "POST"
}
]
}Note:
order ID from Step 2 should match resource.supplementary_data.related_ids.order_id parameter in the webhook payload.
When an order is declined, the CHECKOUT.ORDER.DECLINED webhook passes the error code and message in the most_recent_error parameter of the purchase_unit object.
Alternatively, if your app misses the webhook needed to capture the order, you can get the order capture result by sending a GET call to the Show order details endpoint of the Orders v2 API.
Important: Exercise caution when polling for order capture results using the Show order details endpoint. PayPal enforces rate limits on API requests.
curl -v -X GET https://api-m.sandbox.paypal.com/v2/checkout/orders/5KP92830L1747245S \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <Access-Token>"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:
COMPLETED, which means the order was captured successfully.COMPLETED status is present in the response parameter purchase_units[0].payments.captures[0].up HATEOAS link indicates the order associated with this capture.After a successful payment, notify the buyer of a successful transaction. You can do this by sending a confirmation email.
Test the integration in the PayPal sandbox environment.
Take your application live in the PayPal production environment once testing is successful.