<< Reference Transactions

Implementing Parallel Payments

Parallel payments enables a single buyer to pay multiple merchants in a single checkout session. Parallel payments is available with API version 63.0 and higher.

About Parallel Payments

Parallel payments enables buyers to pay multiple merchants on a marketplace in a single Express Checkout session.

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 and What Is Not Supported

Parallel payments:

  • Supports sales and orders that you later capture with the authorization and capture API operations
  • Supports up to10 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. The following figure is an example of summary information for an online travel agency with payments to an airline and a hotel.



The following figure shows expanded details on the airline purchase.



The following figure shows expanded details on the hotel payment.



Name-Value Pair Syntax Supporting 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 in the range 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

Integrating Parallel Payments by Using the NVP API

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:

  1. Create a unique set of NVP request fields for each payment you will be hosting on your marketplace using the syntax PAYMENTREQUEST_n_NVPREQUESTFIELDNAME where n is a value from 0 - 9.
  2. You are required to pass values in the following Payment Details Type fields in the call to SetExpressCheckout and DoExpressCheckoutPayment. For each of the n payments you host:
    • Pass the value Sale or Order in PAYMENTREQUEST_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.
  3. Use the Payment Details Item Type fields as appropriate in the call to SetExpressCheckout and DoExpressCheckoutPayment to pass data about each payment.

For each payment in the transaction, the DoExpressCheckoutPayment response returns:

  • A PAYMENTINFO_n_PAYMENTREQUESTID value that matches the PAYMENTREQUEST_n_PAYMENTREQUESTID value you passed in the DoExpressCheckoutPayment 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=http://...
&CANCELURL=http://...
&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=Order
&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=Order
&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

Integrating Parallel Payments by Using the SOAP API

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:

  1. Create PaymentDetails as an array of PaymentDetailsType structures, one for each payment you host on your marketplace.
  2. You are required to pass values in the following PaymentDetailsType fields in the call to SetExpressCheckout and DoExpressCheckoutPayment.
    • Pass the value Sale or Order in PaymentAction.
    • 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.
  3. Use PaymentDetailsType and PaymentDetailsItemType fields, as appropriate, in the call to SetExpressCheckout and DoExpressCheckoutPayment 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 the DoExpressCheckoutPayment 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.Order;
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 = "http://item1.com";
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 = "http://item2.com";
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.Order;
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;

Best Practices for Online Travel Agencies Implementing Parallel Payments

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.

Styles of Payment

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.

Table 1. Best practices 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_DESCn 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 the table below.
Order description Use PAYMENTREQUEST_n_DESC to pass merchant-specific messages such as cancellation charges for post-paid offline transactions. This information does not display on the PayPal Review page. Instead, it appears in the buyer's email and in Transaction Details. Your account will not be charged now. You'll later make a payment directly to merchant name. If you cancel the order, ...
Note to merchant Do not use the ALLOWNOTE parameter. It is ignored for parallel payments.

Pass, at least, the primary information 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.

Table 2. Recommended item description information
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

Handling 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.

Instant Update Callback >>