MODULES
...
eCommerce
Payment Gateways
Building a Custom Payment Gateway
26min
prerequisites good understanding of liquid good understanding of javascript good understanding of graphql, including queries, related models, mutations and using mutations to send api calls good understanding of apis introduction siteglide have built integrations between its ecommerce module and three payment gateways stripe integrated with all siteglide ecommerce features paypal for checkout and basic payment forms authorize net for basic payment forms in order to focus on improving features for the payment gateways we have, we won't be building integrations to any other payment gateways at this point instead, this article is intended to help experienced siteglide partners build their own payment gateway integrations payment gateway configuration we've opened up the payment gateway model (model schema name "module 14/payment gateway") to allow you to customize it to suit your own payment gateway field name field id purpose/notes name module field 14/payment gateway 1 payment gateway display name active module field 14/payment gateway 2 boolean only one payment gateway should be active at a time type module field 14/payment gateway 5 payment gateway name in snake case must be unique to your payment gateway in test mode? module field 14/payment gateway 8 boolean custom payment gateway partial path module field 14/payment gateway 19 folder path for the partials you will use to include your gateway code on the form (see next section) basic payment form support the payment flow will look for code in your set custom payment gateway partial path the file it's looking for is {{custom payment gateway partial path}}/basic payment liquid (e g modules/my ecommerce/payment gateways/money take/basic payment ) your code will be included inside the form where in the form layout you see the tag {% include 'ecommerce/basic payment', amount '500', currency 'gbp' %} to access information about your custom payment gateway such as the api keys, you can output {{payment gateway}} into the layout when building you layout consider the following process that needs to be take place set up client side code on page load initially set up any client side html or javascript which the payment gateway requires to be rendered on page load e g elements for securely entering card details define a payment function to run after form validation define a javascript function which will set up and carry out the payment transaction depending on the complexity of your payment gateway, this may include multiple steps you should assign this function to window\ s e payment = my payment function the siteglide form will call this function when validation is complete and it is ready to begin the payment the function definition should return a javascript promise this will allow the siteglide form submit function to wait for the payment to finish, before it continues submitting the form your function definition can use the parameter form this contains the dom element for the form which has been submitted this will be used in the js selectors in the next step to make sure we target the dom in the correct form where more than one form is present on the page window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	}); } all of the following information is available in the html dom and therefore is also accessible via javascript the parameter form from the previous step is used for specificity see the next step r e checking the amount on the server side field javascript selector purpose/notes amount form queryselector('#s e amount') value; the amount of money the customer intends to pay for basic payment forms, this value is editable on the client side if you need the original minimum amount defined against the form, run a server side check currency form s e currency('#s e currency') value; the currency the customer intends to use if you need the original currency set up against the form, use a server side check form configuration id form queryselector('\[name="form id"]') value; this form configuration id can be sent to your own liquid xhr endpoint and used in a graphql query to confirm the details of this form check the minimum value is met siteglide basic payment forms have a "minimum value" defined in the form structure this can be used in two ways as the actual value of products or services when the user is choosing their own price e g a donation form, the minimum price the client will accept in order to not make a loss on the donation when the payment gateway's fees are paid all of our native payment gateways include a server side check to make sure the amount is met so most likely you'll wish to do the same in order to do this your payment gateway should make a request to a liquid page you create the form configuration id and amount should be sent over in your data payload or query parameters the endpoint page should run an admin form configurations query query( 	$fc id id! ){ 	result admin form configurations( 	 filter { 	 id { 	 value $fc id 	 } 	 } 	){ 	 items results{ 	 configuration 	 } 	} } in the results of this query, you'll be able to verify the true minimum amount and currency you can compare these to the s e amount value calculated on the front end depending on the result name you choose the values from this example query of note are {% assign currency = form configuration data result items\[0] configuration properties form payment currency value | downcase %} {% assign minimum amount = form configuration data result items\[0] configuration properties form payment amount value | plus 0 %} on this endpoint, you'll have secure access to these values, so it might make sense to use this same endpoint to send an api call to your payment gateway to set up a payment intent (or complete payment transaction) run a graphql mutation to create a "module 14/payment" model in the database, (with a status of "intent created" or "payment complete" as appropriate) (see below) create a record of the payment in siteglide in order for the client to see a record of the payment in the siteglide admin, you must create a database object with the model schema name of module 14/payment via the javascript in your payment function, store the id of this newly created payment model in the hidden html field #s e order id this way it will be submitted with the form and a relationship will be established between the payment and the case allowing their information to be linked in the admin depending on the complexity of your payment gateway, you may wish to create the payment model at the same time a payment intent is created with the status intent created after capturing funds in a later step, you may wish to update the same model by referring to it by its id, this time giving it the status payment complete create the payment model at the end of the payment process to confirm its success this would immediately be given the status payment complete the key fields you will need to set on the payment model are shown in the following table field name field id purpose / notes payment id module field 14/payment 1 the payment gateway's id for this payment intent gateway id module field 14/payment 2 optional the id of the payment gateway in siteglide that handled this payment gateway type module field 14/payment 4 optional the type of the payment gateway in siteglide that handled this payment status module field 14/payment 5 the status of this payment intent created, payment complete or a custom status of your choice charge id (stripe), capture id (paypal) or use for a similar purpose in another payment gateway module field 14/payment 6 optional currency module field 14/payment 7 the actual currency used amount module field 14/payment 8 the actual amount to be paid resolve any errors to halt both your payment function and form submission and throw errors to the user, you'll need to resolve your payment function's promise with an object containing an errors property an array of objects, each with a message string property note, your promise should not include a reject, as this will not be used window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	 \<! oops, an error > 	 resolve({ errors \[ { message my error } ] }); 	}); } resolve your payment once your my payment function payment function is complete and the funds have been captured, you will want to let the siteglide form submit function finish submitting the form to do this, resolve the promise (without an errors object) window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	 resolve('complete'); \<! form submission continues > 	}); } checkout support the payment flow will look for code in your set custom payment gateway partial path the file it's looking for is {{custom payment gateway partial path}}/checkout liquid (e g modules/my ecommerce/payment gateways/money take/checkout ) your code will be included inside the form where in the form layout you see the tag {% include 'ecommerce/checkout standard' %} to access information about your custom payment gateway such as the api keys, you can output {{payment gateway}} into the layout you may notice that many of the steps are similar to the steps for the basic payment form and you may wish to re use some of your code when building you layout consider the following process that needs to be take place set up client side code on page load initially set up any client side html or javascript which the payment gateway requires to be rendered on page load e g elements for securely entering card details define a payment function to run after form validation define a javascript function which will set up and carry out the payment transaction depending on the complexity of your payment gateway, this may include multiple steps you should assign this function to window\ s e payment = my payment function the siteglide form will call this function when validation is complete and it is ready to begin the payment the function definition should return a javascript promise this will allow the siteglide form submit function to wait for the payment to finish, before it continues submitting the form your function definition can use the parameter form this contains the dom element for the form which has been submitted this will be used in the js selectors in the next step to make sure we target the dom in the correct form where more than one form is present on the page window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	}); } creating an order server side unlike basic payment forms, the amount to be paid is not stored in a javascript variable instead, the amount will be calculated using the contents of the user's cart as the process of converting cart data to an order is lengthy, we've opened up our endpoint for your use setting value url api/ecommerce general/order pre payment processor json method post required headers xreq setrequestheader('content type', 'application/json'); xreq setrequestheader('x csrf token', form queryselector('\[name=\\'authenticity token\\']') value); xreq setrequestheader('x requested with', 'x requested with xmlhttprequest'); body data user id email cart shipping method (id) order id (should normally be blank this is to prevent duplicates) slug discount code (id) var data = { user id form queryselector('#s user id') value, email form queryselector('#s email') value, cart form queryselector('#s e cart') value, shipping method form queryselector('#s e cart shipping') value, order id form queryselector('#s e order id') value, slug form queryselector('#s e slug') value, discount code form queryselector('#s e cart discount') value, form id form queryselector('\[name=parent resource id]') value, hcaptcha form queryselector('\[name=h captcha response]')? form queryselector('\[name=h captcha response]') value null }; response { "order id" "xxxxx", "slug" "xxxxx", "errors" { "example error key" { "id" "xxxxx", "message" "example message" } } } create a relationship between the case and the order after receiving your response, you should store the resulting order id as the value of the hidden field #s e order id this will allow the form to store a relationship between the order and the case when the submission is complete it will also allow you to access that order when needed server side check the order details server side and complete the payment depending on the complexity of your payment gateway, the payment can either be carried out in a single step, or in multiple steps this is up to you you will however have to send at least one api call to your payment gateway from your own liquid endpoint page in order to securely send the details of the order to do this, you'll need to run a graphql query to fetch the order, filtering by its id (the id returned from the order pre payment processor endpoint) and model schema name "module 14/order" the table below shows the fields you can access or update in the order model field name field id purpose/notes user id module field 14/order 1 user email module field 14/order 2 status module field 14/order 3 either intent created, payment complete or a custom status of your choice billing address module field 14/order 4 not yet used by siteglide shipping address module field 14/order 5 not yet used by siteglide payment method module field 14/order 6 not yet used by siteglide you must not use this field to store full card numbers you may choose to store either an id or fingerprint (e g https //stripe com/docs/api/payment methods https //stripe com/docs/api/payment methods ) which your payment gateway uses to reference a payment method shipping method id module field 14/order 7 tracking number module field 14/order 8 not yet used by siteglide total price module field 14/order 9 currency module field 14/order 10 discount code module field 14/order 11 shipping method price module field 14/order 12 shipping method name module field 14/order 13 discount code id module field 14/order 14 resolve any errors to halt both your payment function and form submission and throw errors to the user, you'll need to resolve your payment function's promise with an object containing an errors property an array of objects, each with a message string property note, your promise should not include a reject, as this will not be used window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	 \<! oops, an error > 	 resolve({ errors \[ { message my error } ] }); 	}); } resolve your payment once your my payment function payment function is complete and the funds have been captured, you will want to let the siteglide form submit function finish submitting the form to do this, resolve the promise (without an errors object) window\ s e payment = function(form){ 	return new promise(async function (resolve) { 	 \<! run your payment > 	 resolve('complete'); \<! form submission continues > 	}); }