Implement Parallel Payments
Important: This integration method is deprecated as of January 1, 2017. PayPal continues to support existing merchants using this method, but please be advised new features and enhancements will not be applied to these integrations. For new integrations, see the PayPal Checkout Integration Guide.
Parallel payments enable a single buyer to pay multiple merchants in a single checkout session. Parallel payments are available with API version 63.0 and higher. This feature is not available for Mobile Express Checkout.
About Parallel Payments
In parallel payments, a merchant acts as marketplace host. Consider an online travel agency. An online travel agency marketplace is a typical example of parallel payments in use. The buyer purchases airline tickets and makes reservations from various merchants such as hotels, car rental agencies, and entertainment venues hosted on the site. By implementing parallel payments through Express Checkout, the marketplace host accepts PayPal as a payment method. The host also provides the buyer with a consolidated order on the PayPal Review your information page, summarizing expenses, itineraries, and other supporting information. Buyers see travel information, including cancellation fees, directly from the supplier on the Transaction Details page and in an email message.
What is supported
Parallel payments:
- Supports transactions with payment action SALE
-
Supports up to ten payments in one Express Checkout session
Note: The same merchant can receive multiple payments in one Express Checkout session.
- Does not support use of the Instant Update API (callback)
- Does not support accelerated boarding; however single-payment transactions are still supported
- Does not support parallel billing agreements
Post-integration experience
After you integrate parallel payments, the PayPal cart review area shows summary information for each payment.
Name-value pair syntax for Parallel Payments
The PayPal API uses a special syntax for NVP fields to support parallel payments.
The NVP interface to the PayPal API supports up to a maximum of 10 parallel payments in a transaction. To accommodate this, request fields have the following format, where n is a number from 0
to 9
representing a payment.
PAYMENTREQUEST_n_NVPREQUESTFIELDNAME
The first numbered field in a list of payments starts with n equal to 0
, the second field has n equal to 1
, and so forth.
The response name format is:
PAYMENTREQUEST_n_NVPRESPONSEFIELDNAME
Note: Even if your Express Checkout integration supports single payments only, you must use this format. Specify
n=0
for single payment with version 63.0 or higher of the Express Checkout API.
The payment information returned in the DoExpressCheckoutPayment
response has the same basic format; however, the field name starts with PAYMENTINFO
:
PAYMENTINFO_n_NVPRESPONSEFIELDNAME
The NVP API reference documentation shows the proper format and naming for every NVP field that uses this syntax.
Examples:
The following syntax represents the total amount of the first payment:
PAYMENTREQUEST_0_AMT
The following represents the second line of the name for the third payment:
L_PAYMENTREQUEST_2_NAME1
Use the NVP API to integrate Parallel Payments
To integrate parallel payments by using the NVP API, you need to use the syntax for creating unique NVP request field names and create a unique set of fields for each payment. You also need to set a few required variables.
To integrate parallel payments using the NVP interface to Express Checkout:
- Create a unique set of NVP request fields for each payment you will be hosting on your marketplace using the syntax
PAYMENTREQUEST_n_NVPREQUESTFIELDNAME
wheren
is a value from 0 to 9. -
You are required to pass values in the following Payment Details Type fields in the call to
SetExpressCheckout
andDoExpressCheckoutPayment
. For each of the n payments you host:- Pass the value
Sale
inPAYMENTREQUEST_n_PAYMENTACTION
. - Pass a unique value for
PAYMENTREQUEST_n_PAYMENTREQUESTID
. You will use this value to locate the matching payment response details for that payment. - Pass the merchant's Payer ID (secure merchant account ID) or the merchant's email address in
PAYMENTREQUEST_n_SELLERPAYPALACCOUNTID
.
- Pass the value
- Use the Payment Details Item Type fields as appropriate in the call to
SetExpressCheckout
andDoExpressCheckoutPayment
to pass data about each payment.
For each payment in the transaction, the DoExpressCheckoutPayment
response returns:
- A
PAYMENTINFOn_PAYMENTREQUESTID
value that matches thePAYMENTREQUEST
n_PAYMENTREQUESTID
value you passed in theDoExpressCheckoutPayment
request. Use this value to locate the response data for each payment. -
A
PAYMENTINFO_n_SELLERPAYPALACCOUNTID
. This value is whichever one of the following values was passed in:- The merchant's email address
- The merchant's Payer ID (secure merchant account ID)
If errors are returned, check the response data in the PAYMENTERROR
field for each payment. It is possible that errors are returned only for a subset of the payments, while other payments are successful. For failed payments, you should ask the buyer for an alternate payment method.
Example
The following is an example SetExpressCheckoutrequest
with parallel payments integrated.
Request parameters
[requiredSecurityParameters] &METHOD=SetExpressCheckout &RETURNURL=https://... &CANCELURL=https://... &PAYMENTREQUEST_0_CURRENCYCODE=USD &PAYMENTREQUEST_0_AMT=300 &PAYMENTREQUEST_0_ITEMAMT=200 &PAYMENTREQUEST_0_TAXAMT=100 &PAYMENTREQUEST_0_DESC=Summer Vacation trip &PAYMENTREQUEST_0_INSURANCEAMT=0 &PAYMENTREQUEST_0_SHIPDISCAMT=0 &PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID=seller-139@paypal.com &PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false &PAYMENTREQUEST_0_PAYMENTACTION=Sale &PAYMENTREQUEST_0_PAYMENTREQUESTID=CART26488-PAYMENT0 &PAYMENTREQUEST_1_CURRENCYCODE=USD &PAYMENTREQUEST_1_AMT=200 &PAYMENTREQUEST_1_ITEMAMT=180 &PAYMENTREQUEST_1_SHIPPINGAMT=0 &PAYMENTREQUEST_1_HANDLINGAMT=0 &PAYMENTREQUEST_1_TAXAMT=20 &PAYMENTREQUEST_1_DESC=Summer Vacation trip &PAYMENTREQUEST_1_INSURANCEAMT=0 &PAYMENTREQUEST_1_SHIPDISCAMT=0 &PAYMENTREQUEST_1_SELLERPAYPALACCOUNTID=seller-140@paypal.com &PAYMENTREQUEST_1_INSURANCEOPTIONOFFERED=false &PAYMENTREQUEST_1_PAYMENTACTION=Sale &PAYMENTREQUEST_1_PAYMENTREQUESTID=CART26488-PAYMENT1 &L_PAYMENTREQUEST_0_NAME0=Depart San Jose Feb 12 at 12:10PM Arrive in Baltimore at 10:22PM &L_PAYMENTREQUEST_0_NAME1=Depart Baltimore Feb 15 at 6:13 PM Arrive in San Jose at 10:51 PM &L_PAYMENTREQUEST_0_NUMBER0=Flight 522 &L_PAYMENTREQUEST_0_NUMBER1=Flight 961 &L_PAYMENTREQUEST_0_QTY0=1 &L_PAYMENTREQUEST_0_QTY1=1 &L_PAYMENTREQUEST_0_TAXAMT0=50 &L_PAYMENTREQUEST_0_TAXAMT1=50 &L_PAYMENTREQUEST_0_AMT0=50 &L_PAYMENTREQUEST_0_AMT1=150 &L_PAYMENTREQUEST_0_DESC0=SJC Terminal 1. Flight time: 7 hours 12 minutes &L_PAYMENTREQUEST_0_DESC1=BWI Terminal 1. Flight time: 7 hours 38 minutes &L_PAYMENTREQUEST_1_NAME0=Night(s) stay at 9990 Deereco Road, Timonium, MD 21093 &L_PAYMENTREQUEST_1_NUMBER0=300 &L_PAYMENTREQUEST_1_QTY0=1 &L_PAYMENTREQUEST_1_TAXAMT0=20 &L_PAYMENTREQUEST_1_AMT0=180 &L_PAYMENTREQUEST_1_DESC0=King No-Smoking; Check in after 4:00 PM; Check out by 1:00 PM
Response parameters
[successResponseFields] &TOKEN=EC-17C76533PL706494P
Use the SOAP API to integrate Parallel Payments
Parallel payments uses the PaymentDetailsType
structure to pass data for each merchant receiving payment. You can pass up to a 10 structures in a single call to SetExpressCheckout
and DoExpressCheckoutPayment
.
Note: Be sure to use structure fields that are not marked as "deprecated" in the SOAP API reference documentation.
To integrate parallel payments by using the SOAP interface to Express Checkout:
- Create
PaymentDetails
as an array ofPaymentDetailsType
structures, one for each payment you host on your marketplace. -
You are required to pass values in the following
PaymentDetailsType
fields in the call toSetExpressCheckout
andDoExpressCheckoutPayment
.- Pass the value
Sale
inPaymentAction
. - Pass a unique value in
PaymentRequestID
. You will use this value to locate the matching response details for that payment. - Pass the merchant's Payer ID (secure merchant account ID) or the merchant's email address in the
SellerDetailsType
.PayPalAccountId
field.
- Pass the value
- Use
PaymentDetailsType
andPaymentDetailsItemType
fields, as appropriate, in the call toSetExpressCheckout
andDoExpressCheckoutPayment
to pass data about each payment.
For each payment in the transaction, the DoExpressCheckoutPayment
response returns a PaymentInfoType
structure corresponding to each payment:
- The
PaymentRequestID
will match the value you passed in theDoExpressCheckoutPayment
request. Use this value to locate the response data for each payment. -
SellerDetailsType
.PayPalAccountId
returns one of the following values that was passed in:- The merchant's email address
- The merchant's Payer ID (secure merchant account ID)
If errors occur, check the response data in the PaymentInfo.PaymentError
field. PaymentError
returns the ErrorType
information. It is possible that errors are returned only for a subset of payments, while other payments are successful. For failed payments, you should ask the buyer for an alternate payment method. The following SOAP example sets up the merchants receiving funds:
PaymentDetailsType[] PaymentDetailsArray = new PaymentDetailsType[9];
//*******************************************************
//merchant 1
//*******************************************************
PaymentDetailsType payment1 = new PaymentDetailsType();
payment1.PaymentAction = PaymentActionCodeType.Sale;
payment1.PaymentActionSpecified = true;
payment1.SellerDetails = new SellerDetailsType();
payment1.SellerDetails.PayPalAccountID = "support@1stimagehosting.com";
//set up the line items for the first merchant
PaymentDetailsItemType[] payment1_items_array =
new PaymentDetailsItemType[2];
PaymentDetailsItemType payment1_item1 = new PaymentDetailsItemType();
payment1_item1.Amount = new B asicAmountType();
payment1_item1.Amount.currencyID = CurrencyCodeType.USD;
payment1_item1.Amount.Value = "1.00";
payment1_item1.Description = "payment1_item1_desc";
payment1_item1.Name = "payment1_item1_name";
payment1_item1.Number = "payment1_item1_number";
payment1_item1.ItemURL = "https://example.com/item1";
payment1_item1.Quantity = "3";
payment1_item1.Tax = new BasicAmountType();
payment1_item1.Tax.currencyID = CurrencyCodeType.USD;
payment1_item1.Tax.Value = ".50";
PaymentDetailsItemType payment1_item2 = new PaymentDetailsItemType();
payment1_item2.Amount = new BasicAmountType();
payment1_item2.Amount.currencyID = CurrencyCodeType.USD;
payment1_item2.Amount.Value = "1.00";
payment1_item2.Description = "payment1_item2_desc";
payment1_item2.Name = "payment1_item2_name";
payment1_item2.Number = "payment1_item2_number";
payment1_item2.ItemURL = "https://example.com/item2";
payment1_item2.Quantity = "2";
payment1_item2.Tax = new BasicAmountType();
payment1_item2.Tax.currencyID = CurrencyCodeType.USD;
payment1_item2.Tax.Value = ".25";
payment1_items_array.SetValue(payment1_item1, 0);
payment1_items_array.SetValue(payment1_item2, 1);
//bind the items
payment1.PaymentDetailsItem = payment1_items_array;
//set the totals
decimal tax_total = 0;
decimal item_total = 0;
foreach (PaymentDetailsItemType Key in payment1_items_array)
{
if (Key.Tax != null)
{
tax_total = decimal.Add(tax_total,
decimal.Multiply(decimal.Parse(Key.Tax.Value),
decimal.Parse(Key.Quantity)));
}
if (Key.Amount != null)
{
item_total = decimal.Add(item_total,
decimal.Multiply(decimal.Parse(Key.Amount.Value),
decimal.Parse(Key.Quantity)));
}
}
payment1.ShippingTotal = new BasicAmountType();
payment1.ShippingTotal.currencyID = CurrencyCodeType.USD;
payment1.ShippingTotal.Value = "3.00";
payment1.ItemTotal = new BasicAmountType();
payment1.ItemTotal.currencyID = CurrencyCodeType.USD;
payment1.ItemTotal.Value = item_total.ToString();
payment1.TaxTotal = new BasicAmountType();
payment1.TaxTotal.currencyID = CurrencyCodeType.USD;
payment1 .TaxTotal.Value = tax_total.ToString();
decimal order_total = decimal.Add(decimal.Add(tax_total, item_total),
decimal.Parse(payment1.ShippingTotal.Value));
payment1.OrderTotal = new BasicA mountType();
payment1.OrderTotal.currencyID = CurrencyCodeType.USD;
payment1.OrderTotal.Value = order_total.ToString();
//mandatory for API call
payment1.PaymentRequestID = System.Guid.NewGuid().ToString();
//add the merchants to the array
PaymentDetailsArray.SetValue(payment1, 0);
//*******************************************************
//merchant 2
//*******************************************************
PaymentDetailsType payment2 = new PaymentDetailsType();
payment2.PaymentAction = PaymentActionCodeType.Sale;
payment2.PaymentActionSpecified = true;
payment2.SellerDetails = new SellerDetailsType();
payment2.S ellerDetails.PayPalAccountID = "airline@grupellc.com";
//items for payment2
PaymentDetailsItemType[] payment2_items_array =
new PaymentDetailsItemType[2];
PaymentDetailsItemType payment2_item1 = new PaymentDetailsItemType();
payment2_item1.Amount = new BasicAmountType();
payment2_item1.Amount.currencyID = CurrencyCodeType.USD;
payment2_item1.Amount.Value = "1.00";
payment2_item1.Description = "payment2_item1_desc";
payment2_item1.Name = "payment2_item1_name";
payment2_item1.Number = "payment2_item1_number";
payment2_item1.Quantity = "1";
PaymentDetailsItemType payment2_item2 = new PaymentDetailsItemType();
payment2_item2.Amount = new BasicAmountType();
payment2_item2.Amount.currencyID = CurrencyCodeType.USD;
payment2_item2.Amount.Value = "1.00";
payment2_item2.Description = "payment2_item2_desc";
payment2_item2.Name = "payment2_item2_name";
payment2_item2.Number = "payment2_item2_number";
payment2_item2.Quantity = "1";
payment2_items_array.SetValue(payment2_item1, 0);
payment2_items_array.SetValue(payment2_item2, 1);
//bind the items
payment2.PaymentDetailsItem = payment2_items_array;
//mandatory for API call
payment2.PaymentRequestID = System.Guid.NewGuid().ToString();
//set the totals
decimal tax_total2 = 0;
decimal item_total2 = 0;
foreach (PaymentDetailsItemType Key in payment2_items_array)
{
if (Key.Tax != null)
{
tax_total2 = decimal.Add(tax_total2,
decimal.Multiply(decimal.Parse(Key.Tax.Value),
decimal.Parse(Key.Quantity)));
}
if (Key.Amount != null)
{
item_total2 = decimal.Add(item_total2,
decimal.Multiply(decimal.Parse(Key.Amount.Value),
decimal.Parse(Key.Quantity)));
}
}
payment2.ShippingTotal = new BasicAmountType();
payment2.ShippingTotal.currencyID = CurrencyCodeType.USD;
payment2.ShippingTotal.Value = "3.00";
payment2.ItemTotal = new BasicAmountType();
payment2.ItemTotal.currencyID = CurrencyCodeType.USD;
payment2.ItemTotal.Value = item_total2.ToString();
payment2.TaxTotal = new BasicAmountType();
payment2.TaxTotal.currencyID = CurrencyCodeType.USD;
payment2.TaxTotal.Value = tax_total2.ToString();
decimal order_total2 = decimal.Add(decimal.Add(tax_total2, item_total2),
decimal.Parse(payment2.ShippingTotal.Value));
payment2.OrderTotal = new BasicAmountType();
payment2.OrderTotal.currencyID = CurrencyCodeType.USD;
payment2.OrderTotal.Value = order_total2.ToString();
//add the merchants to the array
PaymentDetailsArray.SetValue(payment2, 1);
//bind the merchants to the request
SetECReq.SetExpressCheckoutRequest.SetExpressCheckoutRequestDetails.Payment
Details = PaymentDetailsArray;
Parallel Payments best practices for online travel agencies
PayPal provides special recommendations to online travel agencies implementing parallel payments to help the buyer complete the payment flow.
Best practices address ways that you as an online travel agency designer can meet the needs of the merchants hosted on your marketplace. The following are examples of special needs of these merchants:
- Merchants providing travel services must offer different styles of payment.
- Merchants offering a service with reservations would want the buyer to know about the cancellation policy and fees.
Payment styles
Merchants hosted by online travel agencies might be using one or more of the following styles of payment:
- Prepaid — Prepaid payments are paid in full at the time of checkout. This style is typical of most online purchases.
- Deposit — A deposit is paid before a service is rendered. Usually it is a flat rate equal to a small percentage of the total, such as a $50.00 deposit on a cruise. The balance is paid offline at some point before the cruise or other service takes place.
- Postpaid — A postpaid expense is not paid until after the service is rendered. Hotel stays, for example, are typically paid at the time of checkout.
Express Checkout displays to the buyer a total amount for all goods or services. For a good buyer experience, you can pass additional information about a deposit or postpaid expense using the Express Checkout PaymentDetailsType
and PaymentDetailsItemType
parameter fields. To eliminate potential buyer confusion, you can let the buyer making a deposit through Express Checkout know, for example, that they will need another payment instrument to complete their purchase.
Payment details
The following table provides PayPal's recommendations for passing payment information.
Item | Best practices | Example content |
---|---|---|
Merchant name | You do not need to pass this data. PayPal obtains the merchant's name that displays on the PayPal Review your information page from the merchant's account record. | PayPal presents the business name, for example, Airline Name. |
Item description | Use L_PAYMENTREQUEST_n_DESCm to pass item description information as a continuous text string. Pass at least the primary information shown in the table below. PayPal recommends that you also pass the secondary information. If the style of payment is a reservation with deposit, pass the deposit only in the item description. |
See table two below. |
Pass, at a minimum, the following primary information. PayPal recommends that you also pass the secondary information. If the style of payment is a reservation with deposit, pass the deposit only in the item description.
Merchant | Item description information |
---|---|
Flight | Primary information Flight No. Departure Location, Date & Time Arrival Location, Date & Time Secondary information Duration Terminal No. Distance |
Hotel | Primary information Location Check-in Date Checkout Date Secondary information Check in Time Room Type Bed Size |
Car | Primary information Pickup Location, Date & Time Drop-off Location, Date & Time Secondary information Car Model & Make Telephone No. |
Insurance | Primary information Insurance Company Name & Description |
Cruise | Primary information Date, Time & Description |
Handle errors
It is possible for some merchant payments to succeed while others fail. Parallel payments creates multiple independent payments, and each payment is subject to its own validation and review.
If part of a payment fails, the ACK
value is PartialSuccess
. To find the error, check the value returned in PAYMENTINFO_n_ERRORCODE
in the response to DoExpressCheckoutPayment
.
Note: If an error is generated by any of the payments in the call to
SetExpressCheckout
, the transaction fails.