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.
- A reference to our CDN hosted JavaScript library.
- A Zip Button element or HTML DOM element
- 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 passsandbox
as the name likevirtualCheckout.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 Name | Type | Optional/Required | Description |
---|---|---|---|
amount | number | Required | The 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. |
shippingAmount | number | Optional | The cost of shipping. |
taxAmount | number | Optional | The cost of taxes. |
currency | Required | The currency in which the customer is transacting, in ISO 4217 format. | |
merchantId | GUID | Required | Your unique ID for your integration. Shared via the Merchant Portal. |
merchantReference | string | Required | This 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. |
customerFirstName | string | Optional | Customer's first name |
customerLastName | string | Optional | Customer's last name |
customerEmail | string | Optional | Customer's email address |
customerPhoneNumber | string | Optional | Customer's phone number |
customerAddressLine1 | string | Optional | Customer's primary address information |
customerAddressLine2 | string | Optional | Customer's secondary address information. May contain - Apartment number, Care of, Attention |
customerCity | string | Optional | Customer's address city |
customerState | string | Optional | Customer's address state, in ISO 3166-2:US format (two letter state code) |
customerPostalCode | string | Optional | 5 digit state postal/zip code |
customerCountry | string | Optional | Customer's address country, in ISO_3166-2 (two letter country code). E.g., US, CA |
allowedShippingLocations | string | Optional | This 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. |
deniedShippingLocations | string | Optional | This 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. |
isDisabled | bool | Optional | For testing purposes. This blocks openCheckout. |
merchantFeeForPaymentPlan | number | Optional | Formerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount. |
forceIFrame | bool | Optional | Default functionality will launch the Zip checkout flow in a pop-up window, unless forceIFrame is set to 'true'. |
checkoutFlow | enum | Optional | Indicates if it’s the standard or express flow. Either “standard” or “express". |
hideOverlay | bool | Optional | Default functionality will present a dark gray overlay behind the pop-up window, unless hideOverlay is set to 'true.' |
test | bool | Optional | Set 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
Properties | Type | Optional/Required | Description |
---|---|---|---|
customer | Customer object | Required | This 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. |
cardholder | Cardholder object | Required | The 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. |
card | Card object | Required | Details about the issued virtual card for your transaction. |
clientToken | string | Optional | Used 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. |
shippingAddress | Address object | Optional | Used in express flows, the customer's shipping address is returned on the result object. |
merchantFeeForPaymentPlan | number | Optional | Formerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount. |
orderId | string | Optional | Sets 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 Name | Type | Optional/Required | Description |
---|---|---|---|
amount | number | Required | The 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. |
order | Json | Optional | The order information in json format. See below for a complete example. |
checkoutFlow | enum | Required | Indicates if it’s the standard or express flow. Either “standard” or “express”. |
forceIFrame | bool | Required | Indicates if the Zip checkout flow will open in an iframe or a modal (popup). |
hideOverlay | bool | Optional | Default 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 Name | Type | Description |
---|---|---|
adjustedOrderAmount | number | The new order amount, including the MFPP fee. |
currency | string | ‘Amount’ currency |
merchantFeeForPaymentPlan | number | The 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 Name | Type | Description |
---|---|---|
amount | number | The new order amount, including the MFPP fee. |
date | string | e.j., Wed Jul 14 2021 00:00:00 GMT-0400 (Eastern Daylight Time) |
sequenceNumber | number | The 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.
-
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. -
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. -
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
- Card Number:
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
Properties | Type | Optional/Required | Description |
---|---|---|---|
firstName | string | Optional | Customer's first name |
lastName | string | Optional | Customer's last name |
string | Optional | Customer's email address | |
phoneNumber | string | Optional | Customer's phone number |
address1 | string | Optional | Customer's primary address information |
address2 | string | Optional | Customer's secondary address information. May contain - Apartment number, Care of, Attention |
city | string | Optional | Customer's address city |
state | string | Optional | Customer's address state, in ISO 3166-2:US format (two letter state code) |
postalCode | string | Optional | 5 digit state postal/zip code |
country | string | Optional | Customer's address country, in ISO_3166-2 (two letter country code) |
Order
Properties | Type | Optional/Required | Description |
---|---|---|---|
amount | number | Required | The 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. |
shippingAmount | number | Optional | The cost of shipping. |
taxAmount | number | Optional | The cost of taxes. |
currency | string | Required | The currency in which the customer is transacting. |
merchantReference | string | Required | The merchant's identifier for the order. This is used to reconcile merchant orders in Zip's system. |
customer | Customer object | Optional | Customer 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. |
merchantFeeForPaymentPlan | number | Optional | Formerly MFPP and now referred to as a installment fee, this is a customer contribution model that includes an incremental order amount. |
lineItems | object Array | Optional | The items in the order. |
Line Item
Properties | Type | Optional/Required | Description |
---|---|---|---|
name | string | Optional | The name of the item |
description | string | Optional | The description of the item. (n.b. the description should have max 100 characters, if it exceeds this limit it will be truncated) |
quantity | number | Optional | The quantity of the item in the order. |
price | number | Optional | The price of 1 item. |
sku | string | Optional | The Stock Keeping Unit number. |
isPreOrder | boolean | Optional | Whether the item is a pre-order item. |
releaseDate | date | Optional | The anticipated shipment date of the item. |
Card
Properties | Type | Optional/Required | Description |
---|---|---|---|
number | string | Required | The virtual Visa card number. |
cvc | string | Required | The card's CVC security code. |
expirationMonth | number | Required | The month expiry of the card. |
expirationYear | number | Required | The year expiry of the card. |
brand | string | Optional | The brand of the credit card. |
Cardholder
Properties | Type | Optional/Required | Description |
---|---|---|---|
name | string | Required | The cardholder's full name. To be used if you collect a single name field for the cardholder information. |
firstName | string | Required | The cardholder's first name. To be used if you collect separate fields for first and last name. |
lastName | string | Required | The cardholder's last name. To be used if you collect separate fields for first and last name. |
address1 | string | Required | Primary billing address information |
address2 | string | Optional | Secondary billing address information. May contain - Apartment number, Care of, Attention. |
city | string | Required | Customer's billing address city. |
state | string | Required | The state of the customer's billing address, in ISO 3166-2:US format. |
postalCode | string | Required | 5 digit state postal/zip code. |
country | string | Required | The country of the customer's billing address in ISO_3166-2 (two letter country code). |
Shipping Address
Properties | Type | Optional/Required | Description |
---|---|---|---|
line1 | string | Required | Primary shipping address information. |
line2 | string | Optional | Secondary shipping address information. May contain - Apartment number, Care of, Attention. |
city | string | Required | The city of the customer's shipping address. |
state | string | Required | The state of the customer's shipping address, in ISO 3166-2:US format. |
country | string | Required | The country of the customer's shipping address in ISO_3166-2 (two letter country code). |
postalCode | string | Required | 5 digit state postal/zip code. |
Checkout Flow
Properties | Type | Description |
---|---|---|
standard | string | |
express | string |
Configuration
Properties | Type | Optional/Required | Description |
---|---|---|---|
forceIframe | boolean | Optional | Forces the Zip checkout to always be rendered in an iframe and never a pop up. |
hideOverlay | boolean | Optional | Hide the gray overlay on the parent site when checkout is launched. |
checkoutFlow | 'standard' / 'express' | Optional | Which UX flow to send the user through. |
returnToMerchant | boolean | Optional | Whether customer returns to Merchant Checkout after Zip Checkout (Opposite of autosubmit). |
test | boolean | Optional | If this a test or not (required to bypass the OTP requirement). |
Updated 2 months ago