Create a custom Mailchimp Open Commerce (formerly Reaction Commerce) payment method

Table of contents

Mailchimp Open Commerce (formerly Reaction Commerce) is primarily a headless GraphQL API with a great storefront and administrator interface that you can fork or use as a base for creating your own, that it’s great for customization given you the choice of how to build the entire store.

Mailchimp Open Commerce came with great documentation (https://docs.reactioncommerce.com/docs/intro) but event the greatest documentation sometimes has missing points that can be explained better in a blog post and that is the case, we are going to cover the process to create a custom payment method.

Before you can start you need to understand a couple of things payment concepts (https://docs.reactioncommerce.com/docs/concepts-payments) and how reaction handles their plugin system (https://docs.reactioncommerce.com/docs/core-plugins-intro), you should also check the how to create a payment provider that it’s the base of this post (https://docs.reactioncommerce.com/docs/how-to-create-a-payment-provider).

After the long introduction finally we can start with the subject that attracts your attention, how can we create a custom payment method, the first thing that we need to understand is that to add a payment method we need 2 pieces of code a reaction API plugin and a react storefront component to show the fields to capture the data provided by the end-user.

Reaction API plugin.

You can use our payment skeleton plugin that we create as a base of your work and I’ll explain the key pieces that you need to customize to accomplish the task. This plugin’s available as npm package https://www.npmjs.com/package/@weknow/reaction-payment-skeleton or in GitHub reaction-payment-skeleton

Package.json

Your plugin needs to be a node package, this doesn’t mean that need to live in npm you can install the package from a local source or Github if you don’t want to publish your package, keep in mind that you need to customize the package information as you need (name, description, version, main, ….).

Graphql Schema

You need to let the API know about your plugin name and what data should be saved for your payment method, for this you need to extend the existing Graphql schema:

reaction-payment-skeleton/src/schemas/schema.graphql

"Data for an reaction payment skeleton"
type ReactionPaymentSkeletonData {
 "The id of the reaction payment payer"
 customerId: String!
 "The name of the reaction payment payer entered by the shopper"
 fullName: String!
 "The card number of the reaction payment payer entered by the shopper"
 cardNumber: String!
}

extend union PaymentData = ReactionPaymentSkeletonData

extend enum PaymentMethodName {
 "reaction payment skeleton"
 reaction_payment_skeleton
}

Extending “PaymentMethodName” will allow you to register your payment method in the options list for the administrator interface.

ReactionPaymentSkeletonData it’s a new PaymentData type that holds all the custom file that you need to save as a result of your payment process.

Be sure to add this union if this part it’s missing your payment data type will not be registered.

extend union PaymentData = ReactionPaymentSkeletonData

You can explore how the schema it’s affected after install your plugin using the Graphql tool http://localhost:3000/graphql-beta

Action Functions

Your plugin should register some functions capture, authorize, refund, and list refunds, only capture and authorize functions are mandatory the rest it’s just in case that your payment method allows refunds.
You can see an example of the functions inside of /reaction-payment-skeleton/src/util, but the most important one it’s the authorize function, this function it’s in charge of all the business logic to connect your payment method with the payment gateway of your choice, here it’s where you are going to receive the data input by the user and make the necessary API calls to validate the data, you need to return a valid payment object, for more detail go to https://docs.reactioncommerce.com/docs/how-to-create-a-payment-provider.

{
   _id: Random.id(),
   address: billingAddress,
   amount,
   createdAt: new Date(),
   data: {
     customerId: Random.id(),
     fullName,
     cardNumber,
     gqlType: "ReactionPaymentSkeletonData" // GraphQL union resolver uses this
   },
   displayName: `Weknow ${fullName}`,
   method: METHOD,
   mode: "authorize",
   name: PAYMENT_METHOD_NAME,
   paymentPluginName: PACKAGE_NAME,
   processor: PROCESSOR,
   riskLevel: "normal",
   shopId,
   status: "created",
   transactionId: Random.id(),
   transactions: []
 }

The custom data that you collect for your payment method going inside of the data object, you can save the data returned by you payment gateway keep in mind that this object it’s referencing the schema that you create in the first step “ReactionPaymentSkeletonData” so all the fields that you need to save for your payment method should be declared in the schema.

In the example that we provide in the repo (/reaction-payment-skeleton/src/util/reactionPaymentSkeletonCreateAuthorizedPayment.js) we only are saving the data provided by the user and generating a random customerId but you need to implement the business logic and save the necessary data of your payment gateway.

You can dive into the other functions in the example that are really simple to understand.

Plugin Registration

You need to register you plugin in the api, for this we need to create a register function that receive the reaction app object where we are going to add a registration object:

{
   label: "Reaction payment skeleton",
   name: "reaction-payment-skeleton",
   version: pkg.version,
   i18n,
   graphQL: {
     schemas
   },
   functionsByType: {
     startup: [startup]
   },
   paymentMethods: [{
     name: "reaction_payment_skeleton",
     canRefund: true,
     displayName: "Reaction Payment Skeleton",
     functions: {
       capturePayment: reactionPaymentSkeletonCapturePayment,
       createAuthorizedPayment: reactionPaymentSkeletonCreateAuthorizedPayment,
       createRefund: reactionPaymentSkeletonCreateRefund,
       listRefunds: reactionPaymentSkeletonListRefunds
     }
   }]
 }

Let’s explain a bit more about what information we need to provide in the registration.

  • Label: Human-readable name of the plugin.
  • Name: Machine name of the plugin.
  • Version: The version of the plugin.
  • I18n: Internationalization, in the example we are passing empty object but you can get more information here (https://docs.reactioncommerce.com/docs/internationalization)
  • Graphql: Here it’s we are going to pass the schema that we create at the beginning of the blog post.
  • functionsByType: you can get more information on https://docs.reactioncommerce.com/docs/core-plugins-intro but basically in the example, we are passing a startup function to add a collection in MongoDB to save our refund data.
  • paymentMethods: here it’s where we declare the new payment method machine name, human-readable name if the payment method can have refunds, and the most important we are passing the action functions (create, authorize, refund, and list refund).

Installation

Well, we have our plugin ready now what?; As I say at the beginning of the post the plugin needs to be a node package but can live on npm.org, GitHub, or local in this blog post we are going to take the local approach but it’s not too different from the other 2.

I am assuming that you are using https://github.com/reactioncommerce/reaction-development-platform for your development.

For the installation we need to:

1.- Create an “api-plugins” folder inside of reaction folder (reaction API system)

cd reaction
mkdir api-plugin

2.- Clone the repo (example one or your custom repo)

git clone git@github.com:weknowinc/reaction-payment-skeleton.git

3.- Edit the reaction/package.json to add your package from the file system.

...
"dependencies": {
  ...
  "reaction-payment-skeleton": "file:./api-plugins/reaction-payment-skeleton"
  ...
}
...

4.- Install the package, you can achieve this task in 2 ways the first one it’s going into the container and run npm install or rebuild the entire reaction container, my preferred way it’s put the container in dev mode then go into it and install the packages this will allow the rebuild of the API when we do the next step (check https://github.com/reactioncommerce/reaction-development-platform for more info).

make init-dev-reaction
docker exec -ti reaction_api_1 bash
// inside the container
npm install

5.- Edit plugin.json (/reaction/plugins.json) file to register our new plugin.

{
 ...
 "addressValidationTest": "@reactioncommerce/api-plugin-address-validation-test",
 "reactionPaymentSkeleton": "reaction-payment-skeleton"
}

6.- If you have the dev mode enable you only need to wait until the API restart if not you can rebuild your container and it’s ready, you can see your plugin working in the administration interface.

http://localhost:4080/settings/payment
Reaction admin payment settings

Storefront Component.

The other important part when you want to add a payment method it’s the storefront component, we are using example-storefront for the frontend application, this component it’s in charge of display the form and information related to the payment option and here is where you are going to capture the information for your payment method.

This it’s just a react component, we have an example component (https://github.com/weknowinc/reaction-payment-storefront-component) that you can use as a base for your custom one.

There are a couple of important props that allow the component to communicated with the outside checkout process.

  /**
   * Called as the form fields are changed
   */
  onChange: PropTypes.func,
  /**
   * When this action's input data switches between being
   * ready for saving and not ready for saving, this will
   * be called with `true` (ready) or `false`
   */
  onReadyForSaveChange: PropTypes.func,
  /**
   * Called with an object value when this component's `submit`
   * method is called. The object may have `data`, `displayName`,
   * and `amount` properties.
   */
  onSubmit: PropTypes.func
The other important part it’s where you format the input data that it’s been sent to the payment plugin.

function buildResult({ fullName = null, cardNumber = null }) {
 return {
   data: { fullName, cardNumber },
   displayName: fullName ? `Weknow reaction payment skeleton for ${fullName}` : null
 };
}

This object it’s been received for the authorize function in the input parameter.

reactionPaymentSkeletonCreateAuthorizedPayment(context, input)

These are the 2 key parts of the component the rest it’s just pure react syntax that you should be able to understand.

Installation

The easiest way to install this component is by cloning the repo (https://github.com/weknowinc/reaction-payment-storefront-component) inside of https://github.com/reactioncommerce/example-storefront/tree/trunk/src/components then you need to edit https://github.com/reactioncommerce/example-storefront/blob/trunk/src/custom/paymentMethods.js file to register this component and use it for the new payment method that you just created.

import ExampleIOUPaymentForm from "@reactioncommerce/components/ExampleIOUPaymentForm/v1";
import StripePaymentInput from "@reactioncommerce/components/StripePaymentInput/v1";
import ReactionPaymentStorefront from '../components/reaction-payment-storefront-component/PaymentStorefrontComponent';

const paymentMethods = [
 {
   displayName: "Credit Card",
   InputComponent: StripePaymentInput,
   name: "stripe_card",
   shouldCollectBillingAddress: true
 },
 {
   displayName: "IOU",
   InputComponent: ExampleIOUPaymentForm,
   name: "iou_example",
   shouldCollectBillingAddress: true
 },
 {
   displayName: "Reaction Payment Skeleton",
   InputComponent: ReactionPaymentStorefront,
   name: "reaction_payment_skeleton",
   shouldCollectBillingAddress: true
 }
];

export default paymentMethods;

Remember run the “example-storefront” in dev mode (make init-dev-example-storefront) this will allow you to have auto-reload, if it is not enabled you should rebuild your container.

I hope that this blog post helps you with your Reaction commerce journey.