Implementing Virtual Card Checkout

Getting Started

This implementation does not require any backend integration. With a few lines of HTML and/or JavaScript, you can accept installment payments in your checkout in less than an hour!

The Virtual Checkout integration is based on three components.

  1. A reference to our CDN hosted JavaScript library.
  2. A Zip Button element or HTML DOM element
  3. JavaScript code to attach one or many callback functionality(s) and customize the payment flow.

There are two different ways to implement the Virtual Checkout (step 2 above). You can either utilize a Zip-branded button element or a custom DOM element. The Zip Button serves as a turnkey solution that is easiest to work with. You may choose to work with a custom DOM element for a more customized integration. Both are fully supported and our button is actually built on top of the same library.

Add the Zip JavaScript Library

To get started, add the Zip JS library to your page. The script can be loaded by adding a tag like <script src="https://cdn.us.zip.co/v1/zip.js" type="text/javascript"></script>. Use the corresponding environment URL below.

The script can be added anywhere in the body or head and can be loaded asynchronously. If loaded asynchronously, be sure to wait for its load to complete before invoking any operations against the library.

There are two environments available and you'll receive a unique Merchant ID for each:

Sandbox (Test) - US: <https://cdn.sand.us.zip.co/v1/zip.js>
Production - US: <https://cdn.us.zip.co/v1/zip.js>

Adding the sandbox script will automatically integrate with our sandbox services for testing. Importing the production script will result in real transactions.

🚧

Environment Switching

An even easier method to switch environments is to use functionality within our scripts! If you use the production script, you can change the environment by calling virtualCheckout.updateEnvironment(name). To call our sandbox environment, you would pass sandbox as the name like virtualCheckout.updateEnvironment('sandbox') before launching the Zip virtual checkout.

📘

Debug Mode

Working through some integration issues with the Zip team? If you add debug=true to your non-production script, you'll be able to enable some logging for the zip.js library that will log messages to your console to help address any issues.

Initiate Checkout

You can initiate checkout via the Zip button or by attaching the click event to a custom HTML DOM element.

Zip Button

The Zip Checkout Button is the easiest way to add a path to Zip in your checkout flow and is well recognized by Zip users.

Add the following code where you want to place the button:
<zip-button id="QPButton" merchantId="[Insert merchant Id]" merchantReference="xxxyyy111" amount="100" currency="USD"></zip-button>

Custom DOM Element

The custom DOM element can be any type of element that supports an onclick event. The below example is a button element but this can be anything from radio buttons, dropdown or image.

<input type="button" id="ZipButton" value="Pay with Zip" />

Set an onComplete callback handler that accepts the virtual card details and uses them as the payment method in your system.

You can find all available attributes for the Zip button in the below table.

Zip Button Attributes

Attribute NameTypeOptional/RequiredDescription
amountnumberRequiredThe maximum amount that can be authorized on the issued virtual card. It should include all fees, taxes, shipping, and discount codes calculated in its value.
shippingAmountnumberOptionalThe cost of shipping.
taxAmountnumberOptionalThe cost of taxes.
currencyRequiredThe currency in which the customer is transacting, in ISO 4217 format.
merchantIdGUIDRequiredYour unique ID for your integration. Shared via the Merchant Portal.
merchantReferencestringRequiredThis is an identifier you will use to reconcile orders made with Zip. Set this to something that is unique for each order that you can preferably reference within your system.
customerFirstNamestringOptionalCustomer's first name
customerLastNamestringOptionalCustomer's last name
customerEmailstringOptionalCustomer's email address
customerPhoneNumberstringOptionalCustomer's phone number
customerAddressLine1stringOptionalCustomer's primary address information
customerAddressLine2stringOptionalCustomer's secondary address information. May contain - Apartment number, Care of, Attention
customerCitystringOptionalCustomer's address city
customerStatestringOptionalCustomer's address state, in ISO 3166-2:US format (two letter state code)
customerPostalCodestringOptional5 digit state postal/zip code
customerCountrystringOptionalCustomer's address country, in ISO_3166-2 (two letter country code). E.g., US, CA
allowedShippingLocationsstringOptionalThis may be utilized in our express flow to restrict shipping only to certain states. Represented by a 2 character abbreviation, such as "NY" for New York. Additionally, the shipping location country cannot be null and must be represented by a two-character ISO 3166 country code, such as "US" for the United States. Example: "WI-US, NY-US, CA" for Wisconsin, USA, New York, USA, and Canada.
deniedShippingLocationsstringOptionalThis may be utilized in our express flow to prevent shipping to certain states. Represented by a 2 character abbreviation, such as "NY" for New York. Additionally, the shipping location country cannot be null and must be represented by a two-character ISO 3166 country code, such as "US" for the United States. Example: "WI-US, NY-US, CA" for Wisconsin, USA, New York, USA, and Canada.
isDisabledboolOptionalFor testing purposes. This blocks openCheckout.
merchantFeeForPaymentPlannumberOptionalFormerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount.
forceIFrameboolOptionalDefault functionality will launch the Zip checkout flow in a pop-up window, unless forceIFrame is set to 'true'.
checkoutFlowenumOptionalIndicates if it’s the standard or express flow. Either “standard” or “express".
hideOverlayboolOptionalDefault functionality will present a dark gray overlay behind the pop-up window, unless hideOverlay is set to 'true.'
testboolOptionalSet to "true" if using the bypass OTP feature in sandbox.

📘

Internet Explorer 11 Support

Because the zip-button utilizes webcomponents, you have to take extra care when initializing it within IE11. In modern browsers, you can start working with the zip-button as soon as the zip.js script executes. However, in IE11, you'll want to configure an event listener on the document for the WebComponentsReady event and then execute your setup code. This is only required if you expect to support IE11. Here's a sample script:

        if (isIe11()) {
            document.addEventListener('WebComponentsReady', function (e) {
                setupQuadPay();
            });
        } else {
            setupQuadPay();
        }

        function isIe11() {
            return !!window.MSInputMethodContext && !!document.documentMode;
        }

Callback Events

onComplete

The onComplete Callback accepts a function as a parameter that will be invoked when a customer successfully completes the Zip checkout. It accepts card and cardholder information and fills in credit card payment details. In the Standard flow, this contains - card, cardholder, customer, order id, and optionally, merchantFeeForPaymentPlan. In the Express flow, the result also includes shippingAddress. This callback may be invoked asynchronously.

❗️

Billing Address

Zip provides a single billing address for all Zip-issued virtual cards. This is the address Zip requires be provided when processing the cards. Please do not hardcode this billing address. While the Zip billing address does not change often, we do periodically update it. If you hardcode the billing address, it will break the integration if we ever update the billing address.

🚧

User Experience Gotchas

  • Don't save the card details with the customer for future purchases as cards are issued on a per-transaction basis.

Code Example:

//On complete callback event
const onCompleteCallback = function (result)
{
    // Fill in your existing credit card form with the details from
    // the dynamically generated Zip single-use card
    document.getElementById("creditCardNumber").value = result.card.number;
    document.getElementById("creditCardCvc").value = result.card.cvc;
    document.getElementById("creditCardExpiryMm").value = result. card.expirationMonth;
    document.getElementById("creditCardExpiryyy").value = result.card.expirationYear;
    document.getElementById("cardholderName").value = result.cardholder.name;

    // Fill in your existing billing address form with details from
    // the card holder.  If your billing address contains first/last name
    // fields, you can leave those as the actual customer values.
    document.getElementById("name").value = result.cardholder.name;
    document.getElementById("addressLine1").value = result.cardholder.address1;
    document.getElementById("addressLine2").value = result.cardholder.address2;
    document.getElementById("city").value = result.cardholder.city;
    document.getElementById("state").value = result.cardholder.state;
    document.getElementById("zipCode").value = result.cardholder.postalCode;
    document.getElementById("country").value = result.cardholder.country;

    //For express version only
    //result.shippingAddress;
    document.getElementById("addressLine1").value = result.shippingAddress.address1;
    document.getElementById("addressLine2").value = result.shippingAddress.address2;
    document.getElementById("city").value = result.shippingAddress.city;
    document.getElementById("state").value = result.shippingAddress.state;
    document.getElementById("zipCode").value = result.shippingAddress.postalCode;
    document.getElementById("country").value = result.shippingAddress.country;
  
   // Fill in customer contact & billing information
    document.getElementById("cust_name").value = result.customer.firstName;
    document.getElementById("cust_name").value = result.customer.lastName;
    document.getElementById("cust_addressLine1").value = result.customer.address1;
    document.getElementById("cust_addressLine2").value = result.customer.address2;
    document.getElementById("cust_city").value = result.customer.city;
    document.getElementById("cust_state").value = result.customer.state;
    document.getElementById("cust_zipCode").value = result.customer.postalCode;
    document.getElementById("cust_country").value = result.customer.country;

    document.getElementById("cust_email").value = result.customer.email;
    document.getElementById("cust_phoneNumber").value = result.customer.phoneNumber;
  
   //Sets the unique ID to use for order creation
   //result.orderId;
  
};

window.quadpay.virtualCheckout.onComplete(onCompleteCallback);

If you are using the custom DOM element, the openCheckout function needs to be called to open the Zip checkout flow. See below for more specific details.

OnComplete Result Object

PropertiesTypeOptional/RequiredDescription
customerCustomer objectRequiredThis contains information the customer enters in the Zip checkout. Merchants sometimes collect this data to store in their systems since the billing information needs to be overridden with the cardholder details.
cardholderCardholder objectRequiredThe cardholder includes the billing information attached to the issued virtual card. This will be different from customer information. If you require email or phone number as part of the billing information, you can use the customer's information for those fields.
cardCard objectRequiredDetails about the issued virtual card for your transaction.
clientTokenstringOptionalUsed in tokenization. When Zip tokenizes the card details, the card object in the onComplete callback response will be null — the card number will not be provided. Instead, a root element clientToken will be populated with a gateway token id.
shippingAddressAddress objectOptionalUsed in express flows, the customer's shipping address is returned on the result object.
merchantFeeForPaymentPlannumberOptionalFormerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount.
orderIdstringOptionalSets the unique ID to use for order creation.

onClose

The onClose callback is invoked whenever the user closes the Zip checkout. This optional callback may happen if the customer closes the window, clicks back to cart, is not approved, or their session times out. The callback may be asynchronous as it is awaited. The callback will accept one parameter:

message (string): Message about why the checkout was closed.

Code Example

///On close callback event
const onCloseCallback = function (result) {

};

window.quadpay.virtualCheckout.onClose(onCloseCallback);

onError

The onError callback handler will be invoked if the parameters supplied to initiate checkout are invalid or missing. This optional callback handles technical errors during loading of the Zip checkout flow. The callback may be asynchronous as it is awaited. The callback will return error messages in the format of an array of strings representing error messages about what validation failed.

Code Example:

// On error callback event
const onErrorCallback = function (result) {
};

//Attaching the on error callback event
window.quadpay.virtualCheckout.onError(onErrorCallback);

More details on all Quadpay.virtualCheckout methods can be found in the below tables.

Checkout Flow Functions

openCheckout (with custom DOM element)

This method is required when a custom DOM element is used and will launch the virtual checkout in a popup window (or iframe depending on provided parameters). The method requires 3 parameters with an optional 4th parameter.

Attribute NameTypeOptional/RequiredDescription
amountnumberRequiredThe maximum amount that can be authorized on the issued virtual card. It should include all fees, taxes, shipping, and discount codes calculated in its value.
orderJsonOptionalThe order information in json format. See below for a complete example.
checkoutFlowenumRequiredIndicates if it’s the standard or express flow. Either “standard” or “express”.
forceIFrameboolRequiredIndicates if the Zip checkout flow will open in an iframe or a modal (popup).
hideOverlayboolOptionalDefault functionality will present a dark gray overlay behind the pop-up window, unless hideOverlay is set to 'true.'

Code Example:

var customButton= document.getElementById("customButton");
customButton.addEventListener("click", () => {
                window.quadpay.virtualCheckout.openCheckout("Insert merchant Id", order, “standard”, false);
 });

focusCheckout

This method allows you to programmatically bring focus to the pop-up window once the virtual checkout has been opened.

Code Example:

window.quadpay.virtualCheckout.focusCheckout();

closeCheckout

This method force-closes the Zip checkout window and ends the user's Zip session.

Code Example:

window.quadpay.virtualCheckout.closeCheckout();

Multi-Page Checkout Handler

The multi page checkout handler is a set of three functions that lets you save a successful onComplete callback result and later retrieve it. The information is saved for the user’s browser session, and will not persist beyond that. This is used primarily when the Express version of virtual checkout is used and a consumer starts the checkout from the Cart or Product page. This means you can retrieve the result on page load of the payment page and populate shopper, shipping address, billing address, and payment information.

1. Save Result

This method accepts the result object returned from the onComplete callback as a parameter that will be invoked when a customer successfully completes the Zip checkout. It is responsible for saving the card, cardholder, customer, shippingAddress, and merchantFeeForPaymentPlan in session storage, for the duration of the user’s browser session. In the event that the checkout process spans multiple pages this information can be saved (and cleared) to allow for checkout information to be available to the merchant through page loads and redirects. This method is designed for express checkout flows.

Code Example:

window.quadpay.virtualCheckout.saveResult(result)

2. Get Complete Result

This method returns the previously stored values from the saveResult() method completed on the previous stage. The result object contains the card, cardholder, customer, shippingAddress, and merchantFeeForPaymentPlan. This allows for checkout information to be accessible to the merchant through page loads and redirects in express checkout flows.

Code Example:

var result = window.quadpay.virtualCheckout.getCompleteResult();

3. Clear Result

This method clears the previously stored values from the saveResult() method from the session storage to ensure that card, cardholder, customer, shippingAddress, and merchantFeeForPaymentPlan information is removed from the browser after multi-page checkout has been completed.

Code Example:

window.quadpay.virtualCheckout.clearResult();

Fee Calculation Functions

calculateMerchantFeeForPaymentPlan

This method calculates the installment fee (formerly MFFP) price depending on country, state, currency and amount.

The calculateMerchantFeeForPaymentPlan() method returns a promise with the below parameters.

Attribute NameTypeDescription
adjustedOrderAmountnumberThe new order amount, including the MFPP fee.
currencystring‘Amount’ currency
merchantFeeForPaymentPlannumberThe per-order fee amount, set as part of the installment fee, a customer contribution model.

Code Example:

window.quadpay.merchantFees.calculateMerchantFeeForPaymentPlan({
merchantId: "Insert merchant Id",
customerState: "NY",
customerCountry: "US",
amount: 100,
currency: "USD"}).then((response) => {
  //response.adjustedOrderAmount
  //response.currency
  //response.merchantFeeForPaymentPlan
});

calculateInstallments

This method takes a number value as a parameter and returns an array with 4 items - each installment amount based on the order amount.

Attribute NameTypeDescription
amountnumberThe new order amount, including the MFPP fee.
datestringe.j., Wed Jul 14 2021 00:00:00 GMT-0400 (Eastern Daylight Time)
sequenceNumbernumberThe installment sequence.

Code Example:

var installments = window.quadpay.calculateInstallments(100);

Order in Json Format

order = {
  amount: 166.00,
  currency: 'USD',
  merchantReference: 'your-unique-order-id',
  shippingAmount: 10.00,
  taxAmount: 5.50,
  customer: {
    address1: '123 street ',
    address2: '',
    city: 'New York',
    state: 'NY',
    postalCode: '10000-4311',
    country: 'US',
    email:'[email protected]',
    firstName: 'John',
    lastName: 'Doe',
    phoneNumber: '+11111111111'
    },
  lineItems: [
    {
    name: 'Item Name 1',
    description: 'Item description 1',
    quantity: 1,
    price: 100.25,
    sku: '8000148',
    isPreOrder: false,
    releaseDate: '2023-10-10',
    },
    {
    name: 'Item Name 2',
    description: 'Item description 2',
    quantity: 1,
    price: 50.25,
    sku: '8000149',
    isPreOrder: false,
    releaseDate: '2023-10-10',
    },
    ]
    };

Testing

Online Checkout

When testing the virtual checkout in non-production scenarios, there are a few key points that will help you get through the Zip checkout flow easily and consistently.

  1. When the checkout launches, you can choose Force New Customer after entering your phone number. This will allow you to have the new customer user experience and bypass some test approval rules.

  2. If you do not have a US phone number, you may use a free SMS service. If you google receive SMS free, you'll find a number of free services with temporary phone numbers that you can use for your testing.

  3. You can use the following test card data on the Zip payment details page:

    • Card Number: 4242 4242 4242 4242 can be used to see a success within our checkout flow
    • Card Number 4000 0000 0000 0002 can be used to see a decline within our checkout flow
    • Card Expiration: 2/25
    • CVC: 222
    • Name/Postal Code: Anything

Virtual Card Transactions

The virtual card that is issued in our sandbox environment is not a card that can be charged via card network rails. You must use the production environment to be issued a valid virtual card that you can authorize/capture/refund against through the card network.

If your gateway/psp supports testing with distinct card values in its sandbox, Zip offers a method to force a card response in onComplete. This will not serve as complete end-to-end testing, as the activity will not flow through the card network to Zip, but will allow testing within your internal gateway/psp connections. Through the use of the below JavaScript code in either your test scripting, or directly through the console after the page has loaded, you can have a gateway/psp valid test card returned from Zip that you can then process through the gateway/psp sandbox.

///set a predefined card
const testCard = {
    number: '4444111122223333',
    cvc: 123,
    expirationMonth: '01',
    expirationYear: '2031',
};
window.quadpay.virtualCheckout.setCardDetails(testCard);

For a standard implementation, we suggest working with the sandbox environment to build the integration. Once complete and certified by Zip, you can update your test environment to use the zip.js production environment to perform card network transactions. If you are not able to connect to our production environment in your non-production environment, there are strategies around query flags, A/B testing, and more that you can utilize to support production roll-out and testing.

Frequently Asked Questions

I am a pay-on-ship merchant. Will this integration work for me?

Yes! Being that it's a standard Visa card, we are able to handle both multiple authorizations and captures.

Since this processes like a standard Visa card, how can I reconcile the orders in my system with the ones Zip processed?

As part of the integration, you are required to set a merchantReference attribute for each checkout. This identifier will be available in our reporting that you can use to link up which Visa orders in your system are Zip virtual checkout orders.

Can the customer apply a gift card or voucher code to a Zip order?

Yes! You can apply the discount to the order and then set the final order amount as an attribute on the Zip button.

What are the authorization and settlement windows?

Merchants must capture part of the order in 7 days at the very latest or reauthorize. And Merchants have 13 days from the order date to complete all captures to coincide with the customer's 2nd Zip installment on day 14.

Can I reuse the virtual card for future customer purchases?

No. Every card generated is unique per transaction. A customer should go through the Zip checkout for each purchase to get a new virtual card to use.

How do I prevent the card from being charged by other merchants?

All of our virtual cards are locked down to your merchant's network information. We decline all credit card transactions unless they come directly from you.

What address do I use for the billing address?

There are a couple of addresses at play with the virtual checkout. The cardholder will contain address information that is associated with the issued virtual Visa card. You will want to use this for the billing address so that authorizations against that card will succeed. You do not need to save this address as it will be sent each time a virtual card is issued. Additionally, we pass the customer's address they provided to Zip via the customer object. You can use this information should you wish to save additional customer details in your system.

When does the customer starting paying their payment plan?

Customers always have 25% (the first installment amount) authorized on their card when they go through the checkout. If the purchase isn't completed for some reason, this transaction is cancelled and the hold on those funds is removed. This also takes 13 days currently, but may be adjusted to happen sooner. The actual payment plan will start processing as soon as there is an authorization against the issued card.

Is there a static BIN range that your virtual cards use?

Yes, there is a BIN range that we can share to be allowlisted with your fraud tools.

Object Model

Customer

PropertiesTypeOptional/RequiredDescription
firstNamestringOptionalCustomer's first name
lastNamestringOptionalCustomer's last name
emailstringOptionalCustomer's email address
phoneNumberstringOptionalCustomer's phone number
address1stringOptionalCustomer's primary address information
address2stringOptionalCustomer's secondary address information. May contain - Apartment number, Care of, Attention
citystringOptionalCustomer's address city
statestringOptionalCustomer's address state, in ISO 3166-2:US format (two letter state code)
postalCodestringOptional5 digit state postal/zip code
countrystringOptionalCustomer's address country, in ISO_3166-2 (two letter country code)

Order

PropertiesTypeOptional/RequiredDescription
amountnumberRequiredThe maximum amount that can be authorized on the issued virtual card. It should include all fees, taxes, shipping, and discount codes calculated in its value.
shippingAmountnumberOptionalThe cost of shipping.
taxAmountnumberOptionalThe cost of taxes.
currencystringRequiredThe currency in which the customer is transacting.
merchantReferencestringRequiredThe merchant's identifier for the order. This is used to reconcile merchant orders in Zip's system.
customerCustomer objectOptionalCustomer object is used to prefill data in the Zip checkout to improve the user experience and conversion. This is typically based on the customer's supplied billing or shipping details entered earlier in the checkout process.
merchantFeeForPaymentPlannumberOptionalFormerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount.
lineItemsobject ArrayOptionalThe items in the order.

Line Item

PropertiesTypeOptional/RequiredDescription
namestringOptionalThe name of the item
descriptionstringOptionalThe description of the item.
(n.b. the description should have max 100 characters, if it exceeds this limit it will be truncated)
quantitynumberOptionalThe quantity of the item in the order.
pricenumberOptionalThe price of 1 item.
skustringOptionalThe Stock Keeping Unit number.
isPreOrderbooleanOptionalWhether the item is a pre-order item.
releaseDatedateOptionalThe anticipated shipment date of the item.

Card

PropertiesTypeOptional/RequiredDescription
numberstringRequiredThe virtual Visa card number.
cvcstringRequiredThe card's CVC security code.
expirationMonthnumberRequiredThe month expiry of the card.
expirationYearnumberRequiredThe year expiry of the card.
brandstringOptionalThe brand of the credit card.

Cardholder

PropertiesTypeOptional/RequiredDescription
namestringRequiredThe cardholder's full name. To be used if you collect a single name field for the cardholder information.
firstNamestringRequiredThe cardholder's first name. To be used if you collect separate fields for first and last name.
lastNamestringRequiredThe cardholder's last name. To be used if you collect separate fields for first and last name.
address1stringRequiredPrimary billing address information
address2stringOptionalSecondary billing address information. May contain - Apartment number, Care of, Attention.
citystringRequiredCustomer's billing address city.
statestringRequiredThe state of the customer's billing address, in ISO 3166-2:US format.
postalCodestringRequired5 digit state postal/zip code.
countrystringRequiredThe country of the customer's billing address in ISO_3166-2 (two letter country code).

Shipping Address

PropertiesTypeOptional/RequiredDescription
line1stringRequiredPrimary shipping address information.
line2stringOptionalSecondary shipping address information. May contain - Apartment number, Care of, Attention.
citystringRequiredThe city of the customer's shipping address.
statestringRequiredThe state of the customer's shipping address, in ISO 3166-2:US format.
countrystringRequiredThe country of the customer's shipping address in ISO_3166-2 (two letter country code).
postalCodestringRequired5 digit state postal/zip code.

Checkout Flow

PropertiesTypeDescription
standardstring
expressstring

Configuration

PropertiesTypeOptional/RequiredDescription
forceIframebooleanOptionalForces the Zip checkout to always be rendered in an iframe and never a pop up.
hideOverlaybooleanOptionalHide the gray overlay on the parent site when checkout is launched.
checkoutFlow'standard' / 'express'OptionalWhich UX flow to send the user through.
returnToMerchantbooleanOptionalWhether customer returns to Merchant Checkout after Zip Checkout (Opposite of autosubmit).
testbooleanOptionalIf this a test or not (required to bypass the OTP requirement).