Create Payment Methods
In general, there are two steps to processing payments:
- Create a payment method
- Create a payment
At a high level, credit card iFrames will tokenize a credit card Payment Method by sending PCI data directly to us and returning a unique payment method token ID to you. Likewise, the Plaid light box tokenizes the card holder's bank account information in order to use it as a payment method.
All tokenized information is entered and stored on our servers, not your platform's, which is why your platform will have a lower PCI scope.
By the end of this article, you will know how to implement our tools to create credit card and ACH Payment Methods.
Tokenize Credit Cards
The process of credit card tokenization follows this journey:
- Customers of your platform navigate to your checkout page, where they will enter payment method information.
- You load WePay's Javascript library on that checkout page, and append the desired payment method tools (i.e. credit card iFrame, Plaid light box, etc) to your user interface.
- Customer enters information, and on submit, you call tokenize.
- WePay JS returns a payment method token ID in a promise.
- You use the token ID in a
/payment_methods
(or/payments
) request to store the token as a Payment Method API object.
Embed The Credit Card iFrame
Here is a sample of how to implement the credit card iFrame, along with the additional billing information that you'll need to collect. To successfully run a test, you'll need the following:
Prerequisites
- Stage app ID
- If you don't already have Stage API credentials, then register for them in the Partner Center
- Insert app ID in the JS file at line 2
- Test card credentials
- Use any credentials listed here
- Paste into the rendered credit card iFrame
#shipping-address {
display: none;
}
//insert your app ID
var appId = "{{insert-your-app-id}}"
var options = {
custom_style:custom_style,
show_labels:true,
show_placeholders:true,
show_error_messages:true,
show_error_messages_when_unfocused:true,
use_one_liner: false
};
document.addEventListener('DOMContentLoaded', function() {
// initialize materialize collapsible
var collapsibleElement = document.querySelector('.collapsible');
var collapsibleInstance = M.Collapsible.init(collapsibleElement);
// initialize materialize material box
var materialBox = document.querySelectorAll('.materialboxed');
var instances = M.Materialbox.init(materialBox);
// load with address open
collapsibleInstance.open(1);
});
// show shipping address if different
function showMe() {
var box = document.getElementById('same');
var vis = (box.checked) ? "block" : "none";
document.getElementById('shipping-address').style.display = vis;
}
// close address section on "next" click
function closeAddress() {
var elems = document.querySelector('.collapsible');
var instances = M.Collapsible.init(elems);
instances.close(1);
}
// open submit section on "next" click
function openSubmit() {
var elems = document.querySelector('.collapsible');
var instances = M.Collapsible.init(elems);
instances.open(2);
}
// credit card iframe styling
var custom_style = {
'styles': {
'base': {
'color': 'grey',
'border': '1px solid grey',
'border-top': 'none',
'border-right': 'none',
'border-left': 'none',
'font-weight': '200',
'font-family': 'Arial',
'padding': '0px',
'margin-bottom': '5px',
':focus': {
'border': '2px solid #4db6ac',
'border-top': 'none',
'border-right': 'none',
'border-left': 'none'
},
'::placeholder': {
'text-transform': 'lowercase',
'color': '#D3D3D3',
'font-size': '17px'
}
},
'invalid': {
'color': '#CD5C5C',
'border-color': '#CD5C5C'
},
'valid': {
'color': '#4db6ac',
'border-color': '#4db6ac'
},
'labels': {
'base': {
'color': 'gray',
'font-family': 'Arial',
'font-size': '13px',
'font-weight': '1',
'text-transform': 'lowercase',
'padding': '0px',
'padding-left': '0px'
}
},
'errors': {
'invalid': {
'color': '#CD5C5C'
}
}
}
};
if (options.use_one_liner) {
custom_style = {
"styles": {
"cc-logo":{
"base":{
"margin":"12px 12px 7px 12px"
}
},
"cc-mask":{
"base":{
"margin":"12px 12px 7px 12px"
}
},
"cvv-icon":{
"base":{
"display":"none"
}
}
}
}
}
//credit card iframe configs
var myAppId = appId;
var apiVersion = "3.0";
var error = WePay.configure("stage", myAppId, apiVersion);
if (error) {
console.log(error);
}
var iframe_container_id = "credit_card_iframe";
var creditCard = WePay.createCreditCardIframe(iframe_container_id, options);
document.getElementById('submit-credit-card-button').addEventListener('click', function (event) {
creditCard.tokenize()
.then(function (response) {
//get the promise response from the console
console.log('response', JSON.stringify(response));
var node = document.createElement('div');
node.innerHTML = JSON.stringify(response);
document.getElementById('token').appendChild(node);
})
.catch(function (error) {
console.log('error', error);
// Move the focus to the first error
if (Array.isArray(error)) {
let key = error[0].target[0];
creditCard.setFocus(key);
}
// display the response on the page for testing purposes; do not launch with this section
var node = document.createElement('div');
node.innerHTML = JSON.stringify(error);
document.getElementById('token').appendChild(node);
});
});
<body>
<div class="container">
<ul class="collapsible popout">
<li>
<div class="collapsible-header"><i class="material-icons">shopping_cart</i>Order</div>
<div class="collapsible-body" id="order-body">
<div class="container">
<div class="row">
<img class="responsive-img col m6 materialboxed"
src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-1671/mop.png" alt="Primo Mop" />
<div class="col m5">
<div class="card-panel blue-grey">
<div class="card-content white-text">
<span class="card-title">Primo Mop</span>
<p>Quantity: 20<br>Price: $15.00 USD</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="card-panel blue-grey">
<div class="card-content white-text center">
<p>Subtotal: 300.00 USD<br>Shipping: $7.50 USD<br>Fees: $2.50 USD</p>
<span class="card-title">Total: $310.00 USD</span>
</div>
</div>
</div>
</div>
</div>
</li>
<li class="active">
<div class="collapsible-header"><i class="material-icons">place</i>Address</div>
<div class="collapsible-body row" id="address-body">
<div class="row">
<div class="col s12">
<div class="card blue-grey">
<div class="card-content white-text">
<span class="card-title">Billing Address</span>
<p>Enter the address associated with your card.</p>
</div>
</div>
</div>
</div>
<div class="container">
<form class="col s12">
<div class="row">
<div class="input-field col s6">
<input placeholder="first name" id="first_name" type="text" class="validate">
<label for="first_name">first name</label>
</div>
<div class="input-field col s6">
<input placeholder="last name" id="last_name" type="text" class="validate">
<label for="last_name">last name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input placeholder="email" id="email" type="text" class="validate">
<label for="email">email</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input placeholder="street address" id="street_address" type="text" class="validate">
<label for="street_address">street address</label>
</div>
<div class="input-field col s6">
<input placeholder="suite / apt." id="suite" type="text" class="validate">
<label for="suite">suite / apt.</label>
</div>
</div>
<div class="row">
<div class="input-field col s4">
<input placeholder="city" id="city" type="text" class="validate">
<label for="city">city</label>
</div>
<div class="input-field col s4">
<input placeholder="state" id="state" type="text" class="validate">
<label for="state">state</label>
</div>
<div class="input-field col s4">
<input placeholder="zip" id="zip" type="text" class="validate">
<label for="zip">zip</label>
</div>
</div>
</form>
<form action="#">
<p>
<label>
<input type="checkbox" id="same" onclick="showMe('shipping-input')" />
<span>Different from shipping address?</span>
</label>
</p>
</form>
</div>
<form class="col s12" id="shipping-address">
<div class="row">
<div class="col s12">
<div class="card blue-grey">
<div class="card-content white-text">
<span class="card-title">Shipping Address</span>
<p>Enter the address where your order will be shipped.</p>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="input-field col s6">
<input placeholder="first name" id="first_name" type="text" class="validate">
<label for="first_name">first name</label>
</div>
<div class="input-field col s6">
<input placeholder="last name" id="last_name" type="text" class="validate">
<label for="last_name">last name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input placeholder="email" id="email" type="text" class="validate">
<label for="email">email</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input placeholder="street address" id="street_address" type="text" class="validate">
<label for="street_address">street address</label>
</div>
<div class="input-field col s6">
<input placeholder="suite / apt." id="suite" type="text" class="validate">
<label for="suite">suite / apt.</label>
</div>
</div>
<div class="row">
<div class="input-field col s4">
<input placeholder="city" id="city" type="text" class="validate">
<label for="city">city</label>
</div>
<div class="input-field col s4">
<input placeholder="state" id="state" type="text" class="validate">
<label for="state">state</label>
</div>
<div class="input-field col s4">
<input placeholder="zip" id="zip" type="text" class="validate">
<label for="zip">zip</label>
</div>
</div>
</form>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action"
onclick="closeAddress(); openSubmit();">Next
<i class="material-icons right">send</i>
</button>
</div>
</li>
<li id="checkout">
<div class="collapsible-header"><i class="material-icons">credit_card</i>Submit</div>
<div class="collapsible-body" id="submit">
<span>
<div class="container">
<div class="row">
<img class="responsive-img" id="accepted-cards"
src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-1671/card-brands.png" border="0"
alt="Accepted Cards" />
</div>
<div class="row">
<div id="credit_card_iframe"></div>
</div>
<div class="row">
<div class="card blue-grey lighten-2">
<div class="card-content white-text">
<p>By clicking submit, you agree to Mop Emporium's <a href="#">refund, cancellation, and return</a>
policy.<br>Orders will be shipped in 2-3 business days, at which time tracking information will be
provided in a confirmation email.</p>
</div>
</div>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action" id="submit-credit-card-button">Submit
<i class="material-icons right">send</i>
</button>
<div id="token"></div>
</div>
</span>
</div>
</li>
</ul>
</div>
<script src="https://cdn.wepay.com/wepay.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</body>
Once a payment method token ID is returned, you have two options:
- Convert the token into a Payment Method (continue reading this article)
- Immediately create a Payment with the token (continue to the Process Payments article)
Whichever route you choose, the token must be used in an API request within 30 minutes of creation, as tokens have a time-to-live (TTL) of 30 minutes.
Verify Credit Cards
Once you have a payment method, a successful $0 authorization must occur before you can use that payment method to create a payment. The $0 auth occurs automatically when thetrigger_verification
parameter is set to true
(default) on the WePay.tokens.create
JS call. Successful authorizations are reflected when the Payment Method API object has "status": "verified"
. Note:
trigger_verification
defaults to true
, and we strongly discourage sending a false
value. Checking the validity of a credit card upon submission reduces the overall number of failed payments on your platform and offers the payer a chance to submit a different, valid payment method while they are still in your UI.Convert Credit Card Tokens
To convert the credit card token into a Payment Method API object, use it in aPOST /payment_methods
request with the following information:{
"type": "credit_card",
"credit_card": {
"card_holder": {
"holder_name": "Lorem Ipsum",
"email": "example@example.com",
"address": {
"line1": "123 Fake St",
"region": "CA",
"postal_code": "90210",
"country": "US"
}
}
},
"token": {
"id": "payment-method-token-id-from-JS-response"
}
}
credit_card
object, as the credit card iFrame only tokenizes PCI data.Designate Or Identify A Card's Funding Source
BETA
Disabling the creation of debit or credit card payment methods is currently a closed BETA offering on API version 3.1.rc.1.3
.
We offer a method of allowing only a credit or debit card funding source during payment method creation. Alternatively, you can simply read the value on the payment method after creation to identify its funding source.
Designate A Funding SourceWhen creating a new payment method with a token from the credit card iframe on
POST /payment_method
or POST /payments
, you can set the optional field card_funding
to debit
or credit
. The debit
value will only successfully create a payment method if the card's funding source is debit, while the credit
value will only successfully create a payment method if the card's funding source is credit. Test credentials
In our stage environment, use the following test credentials in the credit card iFrame:
- 4242424242424242 for credit
- 4000056655665556 for debit
Your request may look like this:
{
"type": "credit_card",
"credit_card": {
"card_holder": {
"holder_name": "John Doe",
"address": {
"postal_code": "94122",
"country": "US"
}
},
"token": {
"id": {"token-id"}
},
"card_funding": {"debit / credit"}
}
}
card_funding
and card details with funding other than the specified value is sent, the error PREPAID_VALIDATION_MISMATCH
will be returned and a payment_method_id
will not be created.Identify A Funding Source
Once a card has been created, you can identify its funding source on the
card_funding
response parameter even if you did not set the parameter during creation. Note that the value can be null
, but this is a rare edge case that will only occur on very new card types / offerings that have not been incorporated in our bin lookups.Disable Prepaid/Gift Card Payment Methods
BETA
Disabling the creation of prepaid card payment methods is currently a closed BETA offering on API version 3.1.rc.1.3
.
We offer you a way to ensure that payment methods aren't being created with prepaid type cards including but not limited to: gift cards, reloadable, virtual, etc.
When creating a new payment method with a token onPOST /payment_method
or POST /payments
, you can set the optional field bin_details.is_prepaid
to false
, indicating that you do not want a payment_method_id
to be created if we validate that the card information submitted is a prepaid type card. Below is an example request:{
"type": "credit_card",
"credit_card": {
"card_holder": {
"holder_name": "John Doe",
"address": {
"postal_code": "94122",
"country": "US"
}
},
"token": {
"id": {"token-id"}
},
"bin_details": {
"is_prepaid": false
}
}
}
is_prepaid
to false
and prepaid card information is sent, the error PREPAID_VALIDATION_MISMATCH
will be returned and a payment_method_id
will not be created to make payments.Reminder: This is an optional field. If
is_prepaid
is not specified in your API request, the default value will be null
and this will allow a payment_method_id
to be created regardless if it is a prepaid card or not.Note
is_prepaid
is set to true
, a payment_method_id
will be created for prepaid type cards only.Tokenize Bank Accounts
Note
POST /payment_methods
, the last_four
variable that is returned will be the last four numbers of the bank account. However, if you have verified your account via Plaid, this might be the last four numbers of your tokenized bank account number (TAN).The process of bank account tokenization via Plaid follows this journey:
- Customers of your platform navigate to your checkout page, where they will enter payment method information.
- You load WePay's Javascript library on that checkout page, and append the desired payment method tools (i.e. credit card iFrame, Plaid light box, etc) to your user interface.
- Customer enters information, and on submit, you call tokenize.
- WePay JS returns a payment method token ID in a promise.
- You use the token ID in a
/payment_methods
(or/payments
) request to store the token as a Payment Method API object.
Embed the Plaid Light Box
Note
Only banks in the US can be used as bank account payment methods.
Leveraging the Plaid modal allows payers to use their bank account to pay by securely logging in to their online banking portal. The login verification is what we call instant verification.
Plaid instant verification also allows your platform to have a customized user experience. The Plaid light box displays the name of your application based on theappID
you provide in line 2 of the JS below. Here is a preview of what the Plaid light box would look like:Prerequisites
- Stage app ID
- Stage login credentials:
- username:
user_good
- password:
pass_good
- username:
The following sample shows how to offer both the credit card iFrame and Plaid.
#shipping-address {
display: none;
}
#complete-screen {
display: none;
}
//insert your app ID
var appId = "{insert_your_app_id}"
var options = {
custom_style:custom_style,
show_labels:true,
show_placeholders:true,
show_error_messages:true,
show_error_messages_when_unfocused:true,
show_error_icon: true,
show_required_asterisk: false,
resize_on_error_message: false,
custom_required_field_error_message: 'THERE WAS AN ERROR',
use_one_liner: false
};
//open plaid modal
function openPlaid() {
if (error) {
console.log(error);
}
var paymentBankLightBox = WePay.createPaymentBankLightBox(
function (data) {
console.log("plaid event:", data);
if (paymentBankLightBox.error_code) {
//error, light box could not be created.
console.error(paymentBankLightBox);
}
},
{ avoid_micro_deposits: true }
);
//tokenize plaid response
paymentBankLightBox
.tokenize()
.then(function (response) {
//payment method token created successfully.
console.log("token_object:", response);
//get the promise response from the console
console.log('response', JSON.stringify(response));
//print the token on the page; REMOVE IN PRODUCTION ENVIRONMENT
var node = document.createElement('div');
var token = ('response', JSON.stringify(response));
node.innerHTML = response["id"];
document.getElementById('tokenBank').appendChild(node);
})
//display completed order part
.then(function closeAddress() {
var elems = document.querySelector('.collapsible');
var instances = M.Collapsible.init(elems);
instances.close(2);
})
.then(function () {
document.getElementById('complete-screen').style.display = "block";
})
.catch(function (error) {
//something went wrong while creating the token.
console.log("error:", error);
});
}
document.addEventListener('DOMContentLoaded', function() {
// initialize materialize collapsible
var collapsibleElement = document.querySelector('.collapsible');
var collapsibleInstance = M.Collapsible.init(collapsibleElement);
// initialize materialize material box
var materialBox = document.querySelectorAll('.materialboxed');
var instances = M.Materialbox.init(materialBox);
// load with address open
collapsibleInstance.open(2);
});
// show shipping address if different
function showMe() {
var box = document.getElementById('same');
var vis = (box.checked) ? "block" : "none";
document.getElementById('shipping-address').style.display = vis;
}
// close address section on "next" click
function closeAddress() {
var elems = document.querySelector('.collapsible');
var instances = M.Collapsible.init(elems);
instances.close(1);
}
// open submit section on "next" click
function openSubmit() {
var elems = document.querySelector('.collapsible');
var instances = M.Collapsible.init(elems);
instances.open(2);
}
// credit card iframe styling
var custom_style = {
'styles': {
'base': {
'color': 'grey',
'border': '1px solid grey',
'border-top': 'none',
'border-right': 'none',
'border-left': 'none',
'font-weight': '200',
'font-family': 'Arial',
'padding': '0px',
':focus': {
'border': '2px solid #4db6ac',
'border-top': 'none',
'border-right': 'none',
'border-left': 'none'
},
'::placeholder': {
'text-transform': 'lowercase',
'color': '#D3D3D3',
'font-size': '17px'
}
},
'invalid': {
'color': '#CD5C5C',
'border-color': '#CD5C5C'
},
'valid': {
'color': '#4db6ac',
'border-color': '#4db6ac'
},
'labels': {
'base': {
'color': 'gray',
'font-family': 'Arial',
'font-size': '13px',
'font-weight': '1',
'text-transform': 'lowercase',
'padding': '0px'
}
},
'errors': {
'invalid': {
'color': '#CD5C5C'
}
},
}
};
if (options.use_one_liner) {
custom_style = {
"styles": {
"cc-logo":{
"base":{
"margin":"12px 12px 7px 12px"
}
},
"cc-mask":{
"base":{
"margin":"12px 12px 7px 12px"
}
},
"cvv-icon":{
"base":{
"display":"none"
}
}
}
}
}
//credit card iframe configs
var myAppId = appId;
var apiVersion = "3.0";
var error = WePay.configure("stage", myAppId, apiVersion);
if (error) {
console.log(error);
}
var iframe_container_id = "credit_card_iframe";
var creditCard = WePay.createCreditCardIframe(iframe_container_id, options);
document.getElementById('submit-credit-card-button').addEventListener('click', function (event) {
creditCard.tokenize()
.then(function (response) {
//get the promise response from the console
console.log('response', JSON.stringify(response));
var node = document.createElement('div');
node.innerHTML = JSON.stringify(response);
document.getElementById('token').appendChild(node);
})
.catch(function (error) {
console.log('error', error);
// Move the focus to the first error
if (Array.isArray(error)) {
let key = error[0].target[0];
creditCard.setFocus(key);
}
// display the response on the page for testing purposes; do not launch with this section
var node = document.createElement('div');
node.innerHTML = JSON.stringify(error);
document.getElementById('token').appendChild(node);
});
});
// ok
<body>
<div class="container">
<ul class="collapsible popout">
<li>
<div class="collapsible-header"><i class="material-icons">shopping_cart</i>Order</div>
<div class="collapsible-body" id="order-body">
<div class="container">
<div class="row">
<img class="responsive-img col m6 materialboxed"
src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-1671/mop.png" alt="Primo Mop" />
<div class="col m5">
<div class="card-panel blue-grey">
<div class="card-content white-text">
<span class="card-title">Primo Mop</span>
<p>Quantity: 20<br>Price: $15.00 USD</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="card-panel blue-grey">
<div class="card-content white-text center">
<p>Subtotal: 300.00 USD<br>Shipping: $7.50 USD<br>Fees: $2.50 USD</p>
<span class="card-title">Total: $310.00 USD</span>
</div>
</div>
</div>
</div>
</div>
</li>
<li class="active">
<div class="collapsible-header"><i class="material-icons">place</i>Address</div>
<div class="collapsible-body row" id="address-body">
<div class="row">
<div class="col s12">
<div class="card blue-grey">
<div class="card-content white-text">
<span class="card-title">Billing Address</span>
<p>Enter the address associated with your card.</p>
</div>
</div>
</div>
</div>
<div class="container">
<form class="col s12">
<div class="row">
<div class="input-field col s6">
<input placeholder="first name" id="first_name" type="text" class="validate">
<label for="first_name">first name</label>
</div>
<div class="input-field col s6">
<input placeholder="last name" id="last_name" type="text" class="validate">
<label for="last_name">last name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input placeholder="email" id="email" type="text" class="validate">
<label for="email">email</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input placeholder="street address" id="street_address" type="text" class="validate">
<label for="street_address">street address</label>
</div>
<div class="input-field col s6">
<input placeholder="suite / apt." id="suite" type="text" class="validate">
<label for="suite">suite / apt.</label>
</div>
</div>
<div class="row">
<div class="input-field col s4">
<input placeholder="city" id="city" type="text" class="validate">
<label for="city">city</label>
</div>
<div class="input-field col s4">
<input placeholder="state" id="state" type="text" class="validate">
<label for="state">state</label>
</div>
<div class="input-field col s4">
<input placeholder="zip" id="zip" type="text" class="validate">
<label for="zip">zip</label>
</div>
</div>
</form>
<form action="#">
<p>
<label>
<input type="checkbox" id="same" onclick="showMe('shipping-input')" />
<span>Different from shipping address?</span>
</label>
</p>
</form>
</div>
<form class="col s12" id="shipping-address">
<div class="row">
<div class="col s12">
<div class="card blue-grey">
<div class="card-content white-text">
<span class="card-title">Shipping Address</span>
<p>Enter the address where your order will be shipped.</p>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="input-field col s6">
<input placeholder="first name" id="first_name" type="text" class="validate">
<label for="first_name">first name</label>
</div>
<div class="input-field col s6">
<input placeholder="last name" id="last_name" type="text" class="validate">
<label for="last_name">last name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input placeholder="email" id="email" type="text" class="validate">
<label for="email">email</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input placeholder="street address" id="street_address" type="text" class="validate">
<label for="street_address">street address</label>
</div>
<div class="input-field col s6">
<input placeholder="suite / apt." id="suite" type="text" class="validate">
<label for="suite">suite / apt.</label>
</div>
</div>
<div class="row">
<div class="input-field col s4">
<input placeholder="city" id="city" type="text" class="validate">
<label for="city">city</label>
</div>
<div class="input-field col s4">
<input placeholder="state" id="state" type="text" class="validate">
<label for="state">state</label>
</div>
<div class="input-field col s4">
<input placeholder="zip" id="zip" type="text" class="validate">
<label for="zip">zip</label>
</div>
</div>
</form>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action"
onclick="closeAddress(); openSubmit();">Next
<i class="material-icons right">send</i>
</button>
</div>
</li>
<li id="checkout">
<div class="collapsible-header"><i class="material-icons">credit_card</i>Submit</div>
<div class="collapsible-body" id="submit">
<span>
<div class="container">
<div class="row">
<img class="responsive-img" id="accepted-cards"
src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-1671/card-brands.png" border="0"
alt="Accepted Cards" />
</div>
<div class="row">
<div id="credit_card_iframe"></div>
</div>
<div class="row">
<a onclick="openPlaid();" id="plaid-modal" class="waves-effect waves-light btn-large"><i
class="material-icons right">account_balance</i>or use bank</a>
</div>
<div class="row">
<div class="card blue-grey lighten-2">
<div class="card-content white-text">
<p>By clicking submit, you agree to Mop Emporium's <a href="#">refund, cancellation, and return</a>
policy.<br>Orders will be shipped in 2-3 business days, at which time tracking information will be
provided in a confirmation email.</p>
</div>
</div>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action" id="submit-credit-card-button">Submit
<i class="material-icons right">send</i>
</button>
<div id="token"></div>
</div>
</span>
</div>
</li>
</ul>
<div class="row">
<div id="tokenBank"></div>
</div>
<div class="row">
<div id="complete-screen" class="row">
<div class="col s12 m12">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">Your order is complete!</span>
<p>We're working on getting your order ready to ship. Once it does, you can log in and track it
here.<br><br><br><br><br></p>
</div>
<div class="card-action">
<a href="#">Track your order</a>
<a href="#">Help</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.wepay.com/wepay.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</body>
var myAppId = "{your_app_id}"; //insert your app ID
var apiVersion = "3.0";
var error = WePay.configure("stage", myAppId, apiVersion);
if (error) {
console.log(error);
}
To force payers through the Plaid flow (rather than the manual payment bank verification flow), create the Plaid modal with the "avoid microdeposits" option like so:
var paymentBankLightBox =
WePay.createPaymentBankLightBox(
function(data) {
console.log('plaid event:', data);
},
{'avoid_micro_deposits': true});
if (paymentBankLightBox.error_code) {
// error, light box could not be created.
console.error(paymentBankLightBox);
}
Next, tokenize the Plaid response:
paymentBankLightBox
.tokenize()
.then(
function(response) {
// payment method token created successfully.
console.log('payment method token:', response);
})
.catch(
function(error) {
// something went wrong while creating the token.
console.log('error:', error);
});
On a successful login through Plaid, the WePay JS will return the following information in a promise:
var response = {
"id":"payment_methods-{token-id}",
"resource":"tokens",
"path":null,
"owner": {
"id":"145948",
"resource":"applications",
"path":null
},
"create_time":12345, //UNIX timestamp
"expire_time":1234530, //UNIX timestamp + 30 days
"api_version":"3.0"
}
Note
Payment Method tokens have a TTL of 30 minutes.
Use Plaid In A Native Mobile App
If you have our iFrame in your native mobile app, you'll need to implement some additional logic for the authentication of certain institutions. For instance, Chase no longer supports in-process webview traffic, meaning the authentication for Plaid must now occur on the Chase website/app. It is expected for more banking institutions to follow suit, but we will use Chase as the example in this section.
You'll need to implement these lines of code in your Android or iOS app to support leaving your app. The requirement to leave your app for authentication is handled by Plaid during institution selection.
Note that we do not currently support app2app or universal links, which would automatically bring the user back to your app once authentication in the banking institution app is complete. Instead, users will see a screen like the one below:
Convert Plaid Tokens
To convert the bank account token into a Payment Method API object, use it in aPOST /payment_methods
request with the following information:{
"token": {
"id": "{payment-method-token-id-from-JS-response}"
}
}
"status": "verified"
, along with an id
. That Payment Method ID can now be used to create a Payment.Manually Verify Bank Accounts
If you configure the Plaid light box withavoid_micro_deposits: false
, then users may opt for the manual verification flow. You can test this flow yourself using the sample provided above.Prerequisites
- Stage app ID
- Select "Manually enter account & routing info" at the bottom of the Plaid light box
- Test payment bank credentials
Once the payment bank has been created, we will send two micro deposits to the linked account. It can take 2-5 business days for these amounts to appear in a bank account's statement, and the micro deposits are only valid for 30 days. Provide a method for the account holder to send your platform the amounts of the micro deposits as means of verification.
Once the user has submitted the micro deposit amounts to you, make aPOST /payment_methods/{id}/verify_bank_deposits
request. You'll use the payment method ID in the endpoint, and the micro deposit amounts in the JSON request body:{
"microdeposits": [10, 55] // Use 10 and 55 in stage
}
"status": "verified"
. Once a payment bank is in a verified status, you can use it in POST /payments
calls to process ACH payments.Note that only two attempts to verify a Payment Method can be made before the object will block further attempts. After the 2nd failed attempt to verify, the following error will return:
{
"error_code": "INVALID_PARAMS",
"error_message": "Invalid parameter(s).",
"details": [{
"target": ["payment_methods"],
"target_type": "HTTP_REQUEST_PATH",
"reason_code": "ID_NOT_FOUND",
"message": "ID 00000000-6261-5553-0000-00000003557c not found."
}]
}
payment_methods.microdeposit_verification_failed
Notification event topic will fire, so long as you've subscribed to the topic. The two-attempt constraint exists to protect against fraud in the ACH/eCheck sphere. If this occurs, the payer should double check their bank account credentials and resubmit payment. To this end, send a
DELETE /payment_methods/id
request to remove the abandoned Payment Method.Now that you have a payment method token, you can process a payment.