Skip to main content
Version: 2.x

Custom Payment Method

Implement a new Front-Commerce Payment method

The payment strategies

Payment can be handled synchronously or asynchronously.

If Front-Commerce allows both strategies to work, we highly recommend you to implement an asynchronous payment method (using

IPN) whenever it is possible.

This will prevent your payments from being rejected later within the provider process without your backend application knowing about it.

Let’s go to the implementation (with IPN)

Front-Commerce allows you to implement your own payment method. New embedded payment methods have to be registered from a GraphQL module. Let’s create a new "PWAy" payment module for a fictive payment provider.

  1. create a new GraphQL module

  2. add a depency upon "Magento2/Checkout" and "Front-Commerce/Payment"

    export default {
    namespace: "Payments/PWAy",
    dependencies: ["Magento2/Checkout", "Front-Commerce/Payment"],
    };
  3. register the payment method from the module’s contextEnhancer

    export default {
    namespace: "Payments/PWAy",
    dependencies: ["Magento2/Checkout", "Front-Commerce/Payment"],
    contextEnhancer: ({ loaders }) => {
    // [...] initialization here
    const loader = new MyPaymentLoader();

    const METHOD_CODE = "pway_awesomecheckout";
    const METHOD_TITLE = "PWAy";
    loaders.Payment.registerEmbeddedPaymentMethod(
    METHOD_CODE,
    METHOD_TITLE,
    // this method is called following the onAuthorize call in the AdditionalDataComponent (see below)
    // e.g. to trigger the payment authorization on the provider (validation and capture will be handled asynchronously by IPN)
    (paymentData, orderId, orderPaymentDetails) => {
    return loader.order(paymentData, orderId, orderPaymentDetails);
    },
    null,
    // The notification processor will handle IPN notifications from the payment provider
    new MyPaymentNotificationProcessor(hipayConfig)
    );

    return {}; // you may export loaders here in case the payment provides custom Queries (to fetch a payment token for instance)
    },
    };
  4. implement your loader's order method to process a payment

export default class MyPaymentLoader {
constructor(...) {
...
}

async order(paymentData, orderId, orderPaymentDetails) {
// process the paymentData and call the payment provider
const paymentStatus = call(...)

// return payment DomainEvents representing what happened in the remote payment system.
// These events are broadcasted by Front-Commerce to the registered external event listeners
// that can use this information to update other remote systems (e.g: adding an Order comment in Magento)
switch(paymentStatus.status) {
case SUCCESS:
// as the IPN handler will handle final authorisation it is often only an authorisation at this step
return new PaymentAuthorized(
new PurchaseIdentifier({ orderId }),
paymentStatus.paymentReference
)
case REFUSED:
return new PaymentRefused(
new PurchaseIdentifier({ orderId }),
paymentStatus.reason
)
}
}
}
  1. Implement the notification processor (for IPN handling)

See how to implement a NotificationProcessor

Handle a sublist of payment methods

You may need to dynamically set the list of payment methods displayed for your module.

This is achieved by registering a replacement handler

export default {
namespace: "Payments/PWAy",
dependencies: ["Magento2/Checkout", "Front-Commerce/Payment"],
contextEnhancer: ({ loaders }) => {
...
loaders.Payment.registerEmbeddedPaymentMethod(
...
);

loaders.Payment.registerMultiplePaymentMethods({
isMethodToReplace: (method) => method.code === METHOD_CODE,
getReplacementMethods: async () => {
try {
// custom call to fetch the allowed payment methods list
const paymentMethods = call(...);
return paymentMethods.map((method) => ({
// this is the method code provided to the checkout workflow
// note that all payment methods must have a common prefix
code: `${prefix}_${method.id}`,
// the displayed payment method name on the checkout page
title: method.description,
// this method is called following the onAuthorize call in the AdditionalDataComponent (see bellow) instead of the default method defined with registerEmbeddedPaymentMethod
callback: (paymentData, orderId, orderPaymentDetails) =>
return loader.order(paymentData, orderId, orderPaymentDetails);
},
} catch (error) {
// handle the error here
return [];
}
},
});
...
},
};

Display the new payment method on the UI

see the custom payment information for details on the front-end implementation of a payment method

see the payment workflows specificities for changes to the current example for each worflow

note

We encourage you to investigate payment- modules' source code from Front-Commerce's core to learn about advanced patterns.