@vendure-community/braintree-plugin
v1.0.1
Published
This plugin enables payments to be processed by [Braintree](https://www.braintreepayments.com/), a popular payment provider.
Readme
Braintree Payment Plugin
This plugin enables payments to be processed by Braintree, a popular payment provider.
Requirements
- You will need to create a Braintree sandbox account as outlined in https://developers.braintreepayments.com/start/overview.
- Then install
braintreeand@types/braintreefrom npm. This plugin was written withv3.xof the Braintree lib.npm install @vendure-community/braintree-plugin braintree npm install -D @types/braintree
Setup
- Add the plugin to your VendureConfig
pluginsarray:import { BraintreePlugin } from '@vendure-community/braintree-plugin'; import { Environment } from 'braintree'; // ... plugins: [ BraintreePlugin.init({ environment: Environment.Sandbox, // This allows saving customer payment // methods with Braintree (see "vaulting" // section below for details) storeCustomersInBraintree: true, }), ] - Create a new PaymentMethod in the Admin UI, and select "Braintree payments" as the handler.
- Fill in the
Merchant ID,Public Key&Private Keyfrom your Braintree sandbox account.
Storefront Usage
The plugin is designed to work with the Braintree drop-in UI. This is a library provided by Braintree which will handle the payment UI for you. You can install it in your storefront project with:
npm install braintree-web-drop-inThe high-level workflow is:
- Generate a "client token" on the server by executing the
generateBraintreeClientTokenmutation which is exposed by this plugin. - Use this client token to instantiate the Braintree Dropin UI.
- Listen for the
"paymentMethodRequestable"event which emitted by the Dropin. - Use the Dropin's
requestPaymentMethod()method to get the required payment metadata. - Pass that metadata to the
addPaymentToOrdermutation. The metadata should be an object of type{ nonce: string; }
Here is an example of how your storefront code will look. Note that this example is attempting to be framework-agnostic, so you'll need to adapt it to fit to your framework of choice.
// The Braintree Dropin instance
let dropin: import('braintree-web-drop-in').Dropin;
// Used to show/hide a "submit" button, which would be bound to the
// `submitPayment()` method below.
let showSubmitButton = false;
// Used to display a "processing..." spinner
let processing = false;
//
// This method would be invoked when the payment screen is mounted/created.
//
async function renderDropin(order: Order, clientToken: string) {
// Lazy load braintree dropin because it has a reference
// to `window` which breaks SSR
dropin = await import('braintree-web-drop-in').then((module) =>
module.default.create({
authorization: clientToken,
// This assumes a div in your view with the corresponding ID
container: '#dropin-container',
card: {
cardholderName: {
required: true,
},
overrides: {},
},
// Additional config is passed here depending on
// which payment methods you have enabled in your
// Braintree account.
paypal: {
flow: 'checkout',
amount: order.totalWithTax / 100,
currency: 'GBP',
},
}),
);
// If you are using the `storeCustomersInBraintree` option, then the
// customer might already have a stored payment method selected as
// soon as the dropin script loads. In this case, show the submit
// button immediately.
if (dropin.isPaymentMethodRequestable()) {
showSubmitButton = true;
}
dropin.on('paymentMethodRequestable', (payload) => {
if (payload.type === 'CreditCard') {
showSubmitButton = true;
}
if (payload.type === 'PayPalAccount') {
this.submitPayment();
}
});
dropin.on('noPaymentMethodRequestable', () => {
// Display an error
});
}
async function generateClientToken() {
const { generateBraintreeClientToken } = await graphQlClient.query(gql`
query GenerateBraintreeClientToken {
generateBraintreeClientToken
}
`);
return generateBraintreeClientToken;
}
async submitPayment() {
if (!dropin.isPaymentMethodRequestable()) {
return;
}
showSubmitButton = false;
processing = true;
const paymentResult = await dropin.requestPaymentMethod();
const { addPaymentToOrder } = await graphQlClient.query(gql`
mutation AddPayment($input: PaymentInput!) {
addPaymentToOrder(input: $input) {
... on Order {
id
payments {
id
amount
errorMessage
method
state
transactionId
createdAt
}
}
... on ErrorResult {
errorCode
message
}
}
}`, {
input: {
method: 'braintree', // The code of you Braintree PaymentMethod
metadata: paymentResult,
},
},
);
switch (addPaymentToOrder?.__typename) {
case 'Order':
// Adding payment succeeded!
break;
case 'OrderStateTransitionError':
case 'OrderPaymentStateError':
case 'PaymentDeclinedError':
case 'PaymentFailedError':
// Display an error to the customer
dropin.clearSelectedPaymentMethod();
}
}Storing Payment Details (Vaulting)
Braintree has a vault feature which allows the secure storage of customer's payment information. Using the vault allows you to offer a faster checkout for repeat customers without needing to worry about how to securely store payment details.
To enable this feature, set the storeCustomersInBraintree option to true.
BraintreePlugin.init({
environment: Environment.Sandbox,
storeCustomersInBraintree: true,
}),Since v1.8, it is possible to override vaulting on a per-payment basis by passing includeCustomerId: false to the generateBraintreeClientToken
mutation:
const { generateBraintreeClientToken } = await graphQlClient.query(gql`
query GenerateBraintreeClientToken($includeCustomerId: Boolean) {
generateBraintreeClientToken(includeCustomerId: $includeCustomerId)
}
`, { includeCustomerId: false });as well as in the metadata of the addPaymentToOrder mutation:
const { addPaymentToOrder } = await graphQlClient.query(gql`
mutation AddPayment($input: PaymentInput!) {
addPaymentToOrder(input: $input) {
...Order
...ErrorResult
}
}`, {
input: {
method: 'braintree',
metadata: {
...paymentResult,
includeCustomerId: false,
},
}
);