BraintreePlugin
BraintreePlugin
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
braintree
and@types/braintree
from npm. This plugin was written withv3.x
of the Braintree lib.yarn add @vendure/payments-plugin braintree yarn add -D @types/braintree
or
npm install @vendure/payments-plugin braintree npm install -D @types/braintree
Setup
- Add the plugin to your VendureConfig
plugins
array:import { BraintreePlugin } from '@vendure/payments-plugin/package/braintree'; import { Environment } from 'braintree'; // ... plugins: [ BraintreePlugin.init({ environment: Environment.Sandbox, // This allows saving customer payment // methods with Braintree 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 Key
from 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:
yarn add braintree-web-drop-in
# or
npm install braintree-web-drop-in
The high-level workflow is:
- Generate a “client token” on the server by executing the
generateBraintreeClientToken
mutation 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
addPaymentToOrder
mutation.
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,
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();
}
}
Signature
class BraintreePlugin {
static static options: BraintreePluginOptions = {};
static init(options: BraintreePluginOptions) => Type<BraintreePlugin>;
}
Members
options
BraintreePluginOptions
init
(options: BraintreePluginOptions) => Type<BraintreePlugin>
BraintreePluginOptions
Options for the Braintree plugin.
Signature
interface BraintreePluginOptions {
environment?: Environment;
storeCustomersInBraintree?: boolean;
}
Members
environment
Environment
Environment.Sandbox
storeCustomersInBraintree
boolean
false
true
, a Customer object
will be created in Braintree, which allows the secure storage of previously-used payment methods.
This is done by adding a custom field to the Customer entity to store the Braintree customer ID,
so switching this on will require a database migration / synchronization.