Skip to main content

Custom UI

This guide walks you through implementing your own card payment form with custom HTML code. We recommend following this guide only if the integration provided by our payment form does not meet your requirements, For more information about the payment form please checkout Basic Integration.

Overview

In this guide, we'll see how to set up a basic payment form to accept credit/debit card payments. The payment flow will be as follows:

  • Tokenize card information temporarely.
  • Use generated token to start the payment.
  • Redirect user to 3DS page.
  • Validate payment result.

Before Starting

Before you start the integration process, make sure you complete these steps:

  1. Sign up for a Moyasar test account at Moyasar's Dashboard.
  2. Get your API Key To authenticate your API request.

For more details about used APIs in this tutorial, please refer to Moyasar API Docs

Due to security considerations, you are not allowed to post user details to your own server and the tokenization process must be started from the client browser directly against Moyasar API.

Integrate Your Payment Form

We need to create a simple HTML form having two different sets of input fields:

  • Hidden input fields are required to authenticate the tokenization request.
  • Visible input fields for user-related data.

Here is the specification of the required data and fields to be included in the submission:

Step 1: Connect the HTML Payment Form

First, set your form’s action attribute to payment create endpoint URL, and set the method attribute to POST

<form action="https://api.moyasar.com/v1/tokens" method="POST">
<button type="submit">Pay</button>
</form>

You are not allowed to use any other action other than the one indicated earlier.

Step 2: Include Authentication Fields

Next, we need to include some required hidden fields for the tokenization process, which are:

  • publishable_api_key: used to identify the merchant account and is safe to include in frontend code. To learn more, check out the Authentication guide.
  • save_only: this fields must be set to true to indicate for the server that this is a checkout token.

Here are the HTML elements need to be added:

<input type="hidden" name="publishable_api_key" value="your_publishable_api_key_here" />
<input type="hidden" name="save_only" value="true" />

Step 3: Include Payment Details Fields

Finally, we need a few visible elements to allow users to enter their payment details. Here are the required fields for credit card payments:

  • name for the card holder name.
  • number for the card number.
  • month for the card expiry month.
  • year for the card expiry year.
  • cvc for the card security value CVC/CVV.

Final Code

<form
id="moyasar-token-form"
accept-charset="UTF-8"
action="https://api.moyasar.com/v1/tokens"
method="POST">
<input type="hidden" name="publishable_api_key" value="your_publishable_api_key_here" />
<input type="hidden" name="save_only" value="true" />

<input type="text" name="name" />
<input type="text" name="number" />
<input type="text" name="month" />
<input type="text" name="year" />
<input type="text" name="cvc" />

<button id="moyasar-payment-button" type="submit">Purchase</button>
</form>

Starting Payment

To start the payment process, we must control the HTML form to create the token then start the payment process from the merchant backend.

The following Javascript code will create the token then pass it to the backend code to start the payment:

async function initiatePayment(event) {
// This line is crucial to prevent the default form behaviour
event.preventDefault();

const form = document.getElementById('moyasar-token-form');
const tokenData = Object.fromEntries(new FormData(form));

// Create temporary checkout token
let response = await fetch('https://api.moyasar.com/v1/tokens', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(tokenData),
});

response = await response.json();

// Send token to merchant backend
let backendResponse = await fetch('https://mystore.example.com/initiate-payment', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// You can add any other information related to your business logic here.
token: response.token,
}),
});

backendResponse = await backendResponse.json();

// Redirect the user to payment page
window.location.href = backendResponse.transaction_url;
}

document.addEventListener('DOMContentLoaded', () => {
document
.getElementById('moyasar-payment-button')
.addEventListener('click', (e) => initiatePayment(e));
});

The previous Javascript code assumes you have your backend on https://mystore.example.com. Your backend must provide an endpoint (e.g. /initiate-payment) for starting the payment process as follows:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class PaymentController extends Controller
{
public function initiatePayment(Request $request)
{
// Assuming you have an Order model where we will check if the order exists first
$order = Order::where('number', $request->input('order_number'))->first();

if (! ($order && $order-payable())) {
return response()
->json(['message' => 'Invalid order'])
->setStatusCode(400);
}

$response = Http::asJson()
->withBasicAuth('pk_live_123', '')
->post('https://api.moyasar.com/v1/payments', [
'amount' => 100,
'currency' => 'SAR',
'description' => 'Test Payment',
'callback_url' => 'https://mystore.example.com/payment-return',
'source' => [
'type' => 'token',
'token' => $request->input('token')
]
])
->throw()
->json();

if ($response['status'] != 'initiated') {
return response()
->json(['message' => 'Payment cannot be initiated: ' . $response['source']['message']])
->setStatusCode(400);
}

return response()->json([
'transaction_url' => $response['source']['transaction_url']
]);
}
}
note

Backend code provided here is only an example and might not be the same backend you are using. You may need to change or adjust the code in order for it to run within your environment.

warning

Error handling is intentionally left out of the previous code examples and it is your responsibility to handle error cases that might arise.

Here is a non-exhaustive list of error cases:

  • Creating token might return a validation or business error 4xx.
  • Creating payment might return a validation or business error 4xx or might return a payment that has status failed.

Verify the Payment

Once the user has completed the payment, they are redirected back to your website or app using the callback_url you provided earlier and the following HTTP query parameters will be appended:

  • id
  • status
  • message
https://www.my-store.com/payments_redirect?id=79cced57-9deb-4c4b-8f48-59c124f79688&status=paid&message=Succeeded

Fetch the payment using its ID through the API, and verify its status, amount, and currency before accepting or placing any user's purchase or, using the ID we have saved in the Javascript example, perform the same verifications.