Create your payment form
Get in touch
Sign in
Create your payment form
Understand how to integrate the payment form into your product

The Solidgate Payment Form allows for the tokenization of sensitive data, manages 3D Secure authentication, and supports multiple payment methods.

It offers dynamic adaptation to card information, supports multiple languages, and is compatible with secure browser versions.

Integration steps

This guide simplifies the process of adding the Payment Form to your website. It covers the preparation of the back end, SDK installation, API instance creation, and merchant data setup. Furthermore, it outlines the initialization of the form using various JavaScript frameworks and how to customize its appearance through parameters and styles.

Back end setup

Begin by setting up your back end, a crucial step for successful implementation. Use the Solidgate SDK to integrate the Payment Form seamlessly into your platforms. This SDK offers intuitive features for transactions and payment form customization. To initiate a charge, supply transaction-specific information through paymentIntent fields.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
  "order_id": "123456",
  "amount": 1020,
  "currency": "USD",
  "order_description": "Premium package",
  "order_items": "item1, item2",
  "order_date": "2020-12-21 11:21:30",
  "order_number": 1,
  "type": "auth",
  "settle_interval": 48,
  "retry_attempt": 1,
  "force3ds": true, 
  "google_pay_merchant_id": "10911390523550288022",
  "google_pay_merchant_name": "Solidgate",
  "apple_pay_merchant_name": "Solidgate",
  "customer_date_of_birth": "2000-11-21",
  "customer_email": "test@solidgate.com",
  "customer_first_name": "John",
  "customer_last_name": "Snow",
  "customer_phone": "380111111111",
  "ip_address": "8.8.8.8",
  "traffic_source": "facebook",
  "transaction_source": "main_menu",
  "purchase_country": "CHN",
  "geo_country": "GBR",
  "geo_city": "New Castle",
  "billing_address": {
    "address": "Street 3D, Apartment 343",
    "country": "United States",
    "state": "Delaware",
    "city": "New Castle",
    "zip_code": "03127"
  },
  "language": "en",
  "website": "https://google.com",
  "device": "iPhone 8 iOS 12.0",
  "platform": "WEB",
  "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (HTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",    
  "order_metadata": {
    "coupon_code": "NY2018",
    "partner_id": "123989"
  },
  "success_url": "https://merchant.example/success",
  "fail_url": "https://merchant.example/fail"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
  "order_id": "123456",
  "product_price_id": "faf3b86a-1fe6-4ae5-84d4-ab0651d75db2",
  "coupon_id": "eb4c6e93-4c53-447a-b215-5d5786af9844",
  "customer_account_id": "4dad42f808",   
  "currency": "USD",
  "order_description": "Premium package",
  "order_items": "item1, item2",
  "order_date": "2020-12-21 11:21:30",
  "order_number": 1,
  "type": "auth",
  "settle_interval": 48,
  "retry_attempt": 1,
  "force3ds": true,
  "google_pay_merchant_id": "10911390523550288022",
  "google_pay_merchant_name": "Solidgate",
  "apple_pay_merchant_name": "Solidgate",
  "customer_date_of_birth": "2000-11-21",
  "customer_email": "test@solidgate.com",
  "customer_first_name": "John",
  "customer_last_name": "Snow",
  "customer_phone": "380111111111",
  "ip_address": "8.8.8.8",
  "traffic_source": "facebook",
  "transaction_source": "main_menu",
  "purchase_country": "CHN",
  "geo_country": "GBR",
  "geo_city": "New Castle",
  "billing_address": {
    "address": "Street 3D, Apartment 343",
    "country": "United States",
    "state": "Delaware",
    "city": "New Castle",
    "zip_code": "03127"
  },
  "language": "en",
  "website": "https://google.com",
  "device": "iPhone 8 iOS 12.0",
  "platform": "WEB",
  "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (HTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
  "order_metadata": {
    "coupon_code": "NY2018",
    "partner_id": "123989"
  },
  "success_url": "https://merchant.example/success",
  "fail_url": "https://merchant.example/fail"
}

With SDK

Step 1 Install SDK

Install the Solidgate SDK in your environment:

1
composer require solidgate/php-sdk
1
npm install @solidgate/node-sdk
1
go get github.com/solidgate-tech/go-sdk
1
pip3 install solidgate-card-sdk

Step 2 Create SDK instance

To interact with the Solidgate API, create an API instance by calling the API constructor function, passing your Public public_key and Secret secret_key keys as arguments.

1
2
3
4
5
<?php

use SolidGate\API\Api;

$api = new Api('public_key', 'secret_key');
1
2
3
const solidGate = require('@solidgate/node-sdk');

let api = new solidGate.Api("public_key", "private_key");
1
solidgateSdk := solidgate.NewSolidGateApi("public_key", "secret_key")
1
val api = Api(HttpClient(), Credentials("public_key", "secret_key"))
1
2
3
from solidgate import ApiClient

client = ApiClient("public_key", "secret_key")

Step 3 Form intent data

To create a charge, you must provide transaction-related information. This information is contained in a FormInitDTO object, which is created by invoking the formMerchantData function on your API instance. In the example code, this function is called with the paymentIntent fields.

1
2
3
4
5
6
7
<?php

use SolidGate\API\Api;

$api = new Api('public_key', 'secret_key');

$response = $api->formMerchantData(['JSON payment intent // fill as described in documentation']);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const solidGate = require('@solidgate/node-sdk');

let api = new solidGate.Api("public_key", "private_key");

let paymentIntentData = {
    /// fill it as described in documentation
}
let merchantData = api.formMerchantData(paymentIntentData);

const dataToFront = merchantData.toObject()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
    "encoding/json"
    "fmt"

    solidgate "github.com/solidgate-tech/go-sdk"
)

func main() {
    solidgateSdk := NewSolidGateApi("public_key", "secret_key")
    paymentIntent = PaymentIntent{} // fill as described in documentation
    paymentIntentBytes, err := json.Marshal(paymentIntent)

    if err != nil {
        fmt.Print(err)
    }

    formInitDto, err := solidgateSdk.FormMerchantData(paymentIntentBytes)

    if err != nil {
        fmt.Print(err)
    }

    // ...  
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
val api = Api(HttpClient(), Credentials("public_key", "secret_key"))

val attributes = Attributes(mapOf(
    "amount" to 123,
    "currency" to "USD",
    "customer_email" to "testuser@example.com",
    "ip_address" to "8.8.8.8",
    "order_description" to "Test subscription",
    "order_id" to "order12345",
    "platform" to "WEB"
))

val response = api.formMerchantData(attributes)
1
2
3
4
5
6
7
from solidgate import ApiClient

client = ApiClient("public_key", "secret_key")

responseDTO = client.form_merchant_data({
    payment_intent_dict  // fill as described in documentation
})

Step 4 Pass generated data to front end

After you have set up the FormInitDTO and turned it into a simple object, you can effortlessly integrate it into your front-end code by transmitting the paymentIntent encrypted String (during form initialization).

Without SDK

An alternative option for integrating the payment form exists without utilizing the Solidgate SDK.

  1. Back end preparation
    Set up server-side code to facilitate communication with the Solidgate API, using your preferred programming language and libraries.
  2. Create DTO
    Create a JSON object that includes the required fields for a payment transaction and encrypt it.
  3. Generate merchant data
    After successfully encrypting the paymentIntent, pass it to your front-end code for further processing, such as form initialization.
  4. Signature generation
    To integrate the Guide
    Learn to authenticate API requests and fix validation errors effectively.
    signature parameter
    process with the encryption steps for initializing form, the flow for paymentIntent encrypted String

    1. User action: convert data
      The user initiates a transaction, providing the necessary payment data.
    2. Token generation: generate initialization vector (IV)
      Internally, a secure random IV is created, corresponding to the required size for the encryption process.
    3. Key preparation: create encryption key
      The system extracts the needed size from the provided secret key and converts it into the appropriate format for encryption.
    4. Encryption setup: initialize cipher
      A cipher object is established using the selected encryption algorithm, mode, and padding. The cipher is then initialized in encryption mode with the key and IV.
    5. Data processing: encrypt data
      The input data, now in the form of a JSON string, is converted to bytes. The initialized cipher is used to encrypt this data.
    6. Integration step: combine IV and encrypted data
      The IV and the encrypted data are combined into a single-byte array, integrating the necessary components for secure transmission.
      *There are specific nuances to consider to ensure compatibility and security, such as using the pycryptodome library for Python 3 environments.
    7. Encoding for transmission: Base64 encoding
      This combined data (IV and encrypted data) is encoded into a Base64 string, preparing it for safe transmission over the network.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php

class PaymentIntentGenerator
{
    private string $secretKey;

    public function __construct(string $secretKey)
    {
        $this->secretKey = $secretKey;
    }

    private function generateEncryptedFormData(array $attributes): string
    {
        $attributes = json_encode($attributes);
        $secretKey = substr($this->secretKey, 0, 32);

        $ivLen = openssl_cipher_iv_length('aes-256-cbc');
        $iv = openssl_random_pseudo_bytes($ivLen);

        $encrypt = openssl_encrypt($attributes, 'aes-256-cbc', $secretKey, OPENSSL_RAW_DATA, $iv);

        return $this->base64UrlEncode($iv . $encrypt);
    }

    private function base64UrlEncode($data): string
    {
        return str_replace(['+', '/'], ['-', '_'], base64_encode($data));
    }

    public function testGenerateEncryptedFormData(): void
    {
        $attributes = [
            "order_id"          => "order_id",
            "amount"            => 1100,
            "currency"          => "USD",
            "order_description" => "description",
            "customer_email"    => "test@yourdomain.com",
            "ip_address"        => "8.8.8.8",
            "platform"          => "WEB",
        ];

        $encryptedFormData = $this->generateEncryptedFormData($attributes);
        echo "Encrypted Form Data: " . $encryptedFormData . PHP_EOL;
    }
}

$yourClassInstance = new PaymentIntentGenerator("api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx");
$yourClassInstance->testGenerateEncryptedFormData();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const crypto = require('crypto');

class PaymentIntentGenerator {
    constructor() {
        this.secretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";
    }

    generateIntent() {
        const intent = '{"order_id":"order_id","amount":1100,"currency":"USD","order_description":"description","customer_email":"test@solidgate.com","ip_address":"8.8.8.8","platform":"WEB"}';
        const paymentIntent = this.encryptPaymentIntent(intent, this.secretKey);

        console.log("Generated Payment Intent:");
        console.log(paymentIntent);
    }

    encryptPaymentIntent(value, secretKey) {
        const iv = crypto.randomBytes(16);
        const aesKey = Buffer.from(secretKey.substring(0, 32), 'utf-8');
        const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv);

        let encryptedBytes = cipher.update(value, 'utf-8', 'base64');
        encryptedBytes += cipher.final('base64');

        const encryptedWithIV = Buffer.concat([iv, Buffer.from(encryptedBytes, 'base64')]);

        const base64Encoded = encryptedWithIV.toString('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_');

        return base64Encoded;
    }
}

const generator = new PaymentIntentGenerator();
generator.generateIntent();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class PaymentIntentGenerator {
  private final String secretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";

  public void generateIntent() throws Exception {
    String intent = "{\"order_id\":\"order_id\",\"amount\":1100,\"currency\":\"USD\",\"order_description\":\"description\",\"customer_email\":\"test@solidgate.com\",\"ip_address\":\"8.8.8.8\",\"platform\":\"WEB\"}";
    String paymentIntent = encryptPaymentIntent(intent, secretKey);

    System.out.println("Generated Payment Intent:");
    System.out.println(paymentIntent);
  }

  public String encryptPaymentIntent(String value, String secretKey) throws Exception {
    byte[] iv = new byte[16];
    new SecureRandom().nextBytes(iv);

    SecretKeySpec aesKey = new SecretKeySpec(secretKey.substring(0, 32).getBytes(StandardCharsets.UTF_8), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(iv));

    byte[] encryptedBytes = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
    byte[] encryptedWithIV = new byte[iv.length + encryptedBytes.length];
    System.arraycopy(iv, 0, encryptedWithIV, 0, iv.length);
    System.arraycopy(encryptedBytes, 0, encryptedWithIV, iv.length, encryptedBytes.length);

    return Base64.getEncoder().encodeToString(encryptedWithIV)
        .replace("+", "-")
        .replace("/", "_");
  }

  public static void main(String[] args) throws Exception {
    PaymentIntentGenerator generator = new PaymentIntentGenerator();
    generator.generateIntent();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Security.Cryptography;
using System.Text;

class PaymentIntentGenerator
{
    private string secretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";

    public void GenerateIntent()
    {
        string intent = "{\"order_id\":\"order_id\",\"amount\":1100,\"currency\":\"USD\",\"order_description\":\"description\",\"customer_email\":\"test@solidgate.com\",\"ip_address\":\"8.8.8.8\",\"platform\":\"WEB\"}";
        string paymentIntent = EncryptPaymentIntent(intent, secretKey);

        Console.WriteLine("Generated Payment Intent:");
        Console.WriteLine(paymentIntent);
    }

    public string EncryptPaymentIntent(string value, string secretKey)
    {
        byte[] iv = new byte[16];
        new Random().NextBytes(iv);

        using (RijndaelManaged aesAlg = new RijndaelManaged())
        {
            aesAlg.Key = Encoding.UTF8.GetBytes(secretKey.Substring(0, 32));
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;

            using (ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, iv))
            {
                byte[] encryptedBytes = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(value), 0, value.Length);
                byte[] encryptedWithIV = new byte[iv.Length + encryptedBytes.Length];
                Array.Copy(iv, 0, encryptedWithIV, 0, iv.Length);
                Array.Copy(encryptedBytes, 0, encryptedWithIV, iv.Length, encryptedBytes.Length);
                string base64Encoded = Convert.ToBase64String(encryptedWithIV).Replace("+", "-").Replace("/", "_");
                return base64Encoded;
            }
        }
    }

    static void Main()
    {
        PaymentIntentGenerator generator = new PaymentIntentGenerator();
        generator.GenerateIntent();
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io"
)

type PaymentIntentGenerator struct {
    key []byte
}

func NewPaymentIntentGenerator(secretKey string) *PaymentIntentGenerator {
    return &PaymentIntentGenerator{
        key: []byte(secretKey)[:32],
    }
}

func (p *PaymentIntentGenerator) GenerateIntent() (string, error) {
    intent := map[string]interface{}{
        "order_id":          "order_id",
        "amount":            1100,
        "currency":          "USD",
        "order_description": "description",
        "customer_email":    "test@solidgate.com",
        "ip_address":        "8.8.8.8",
        "platform":          "WEB",
    }

    intentBytes, err := json.Marshal(intent)
    if err != nil {
        return "", fmt.Errorf("marshal: %w", err)
    }

    paymentIntent, err := p.encrypt(intentBytes)
    if err != nil {
        return "", fmt.Errorf("encrypt: %w", err)
    }

    return base64.URLEncoding.EncodeToString(paymentIntent), nil
}

func (p *PaymentIntentGenerator) encrypt(raw []byte) ([]byte, error) {
    data := p.pad(raw)

    block, err := aes.NewCipher(p.key)

    if err != nil {
        return []byte(nil), fmt.Errorf("new cipher: %w", err)
    }

    cipherText := make([]byte, aes.BlockSize+len(data))
    iv := cipherText[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, fmt.Errorf("io read: %w", err)
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(cipherText[aes.BlockSize:], data)

    return cipherText, nil
}

func (p *PaymentIntentGenerator) pad(data []byte) []byte {
    n := aes.BlockSize - (len(data) % aes.BlockSize)
    pb := make([]byte, len(data)+n)

    copy(pb, data)
    copy(pb[len(data):], bytes.Repeat([]byte{byte(n)}, n))

    return pb
}

func main() {
    generator := NewPaymentIntentGenerator("api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx")
    intent, err := generator.GenerateIntent()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("Generated Payment Intent: %s", intent)
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import java.util.Base64

class PaymentIntentGenerator {
    private val secretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx"

    fun generateIntent() {
        val intent = "{\"order_id\":\"order_id\",\"amount\":1100,\"currency\":\"USD\",\"order_description\":\"description\",\"customer_email\":\"test@solidgate.com\",\"ip_address\":\"8.8.8.8\",\"platform\":\"WEB\"}"
        val paymentIntent = encryptPaymentIntent(intent, secretKey)

        println("Generated Payment Intent:")
        println(paymentIntent)
    }

    fun encryptPaymentIntent(value: String, secretKey: String): String {
        val iv = ByteArray(16)
        SecureRandom().nextBytes(iv)

        val aesKey = SecretKeySpec(secretKey.substring(0, 32).toByteArray(charset("UTF-8")), "AES")
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        cipher.init(Cipher.ENCRYPT_MODE, aesKey, IvParameterSpec(iv))

        val encryptedBytes = cipher.doFinal(value.toByteArray(charset("UTF-8")))
        val encryptedWithIV = ByteArray(iv.size + encryptedBytes.size)
        System.arraycopy(iv, 0, encryptedWithIV, 0, iv.size)
        System.arraycopy(encryptedBytes, 0, encryptedWithIV, iv.size, encryptedBytes.size)

        val base64Encoded = Base64.getEncoder().encodeToString(encryptedWithIV)
            .replace("+", "-")
            .replace("/", "_")

        return base64Encoded
    }
}

fun main() {
    val generator = PaymentIntentGenerator()
    generator.generateIntent()
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes
import base64
import json


class PaymentIntentGenerator:
    def __init__(self):
        self.__key = b'api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx'[:32]
        self.__block_size = 16

    def generate_intent(self):
        intent = {
            "order_id": "order_id",
            "amount": 1100,
            "currency": "USD",
            "order_description": "description",
            "customer_email": "test@solidgate.com",
            "ip_address": "8.8.8.8",
            "platform": "WEB"
        }
        intent_str = json.dumps(intent)
        payment_intent = self.encrypt(intent_str)

        print("Generated Payment Intent:")
        print(payment_intent)

    def encrypt(self, raw):
        raw = pad(raw.encode(), self.__block_size, style='pkcs7')
        iv = get_random_bytes(self.__block_size)
        cipher = AES.new(self.__key, AES.MODE_CBC, iv)
        return base64.urlsafe_b64encode(iv + cipher.encrypt(raw)).decode('utf-8')


if __name__ == "__main__":
    generator = PaymentIntentGenerator()
    generator.generate_intent()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
require 'openssl'
require 'base64'
require 'json'

class PaymentIntentGenerator
  IV_LENGTH = 16
  KEY_LENGTH = 32

  def initialize
    @secret_key = 'api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx'[0, KEY_LENGTH]
  end

  def generate_intent
    intent = {
      order_id: 'order_id',
      amount: 1100,
      currency: 'USD',
      order_description: 'description',
      customer_email: 'test@solidgate.com',
      ip_address: '8.8.8.8',
      platform: 'WEB'
    }

    intent_str = intent.to_json
    payment_intent = encrypt_payload(intent_str)

    puts 'Generated Payment Intent:'
    puts payment_intent
  end

  private

  def encrypt_payload(attributes)
    key = @secret_key
    iv = OpenSSL::Random.random_bytes(IV_LENGTH)
    cipher = OpenSSL::Cipher.new('aes-256-cbc')
    cipher.encrypt
    cipher.key = key
    cipher.iv = iv
    encrypted = cipher.update(attributes) + cipher.final

    base64_encoded = Base64.urlsafe_encode64(iv + encrypted)
    base64_encoded.gsub('+', '-').gsub('/', '_')
  end
end

generator = PaymentIntentGenerator.new
generator.generate_intent

Front end SDK setup

The Solidgate SDK provides a pre-built user interface component for payments. It assists in tokenizing customer data, ensuring that card information never interacts with the merchant’s website.

When starting with the Payment Form, you can use our official payment form SDK wrapper for different JavaScript frameworks:

These integrations automatically load solid-form.js and offer a convenient interface for form operation. Furthermore, you can use manual installation.

Alternatively, manually add the payment form SDK to your checkout page by inserting the script tag at the end of the HTML file’s <body> tag. Utilize the Solidgate CDN-hosted form for easy integration, ensuring the latest version and preventing potential issues from script modification.

1
<script src="https://cdn.solidgate.com/js/solid-form.js"></script>
warning
Check the console for a warning that Solidgate PaymentFormSdk is already initialized. If this warning appears, solid-form.js has most likely been loaded and executed twice. Remove the unnecessary connection.

Next, create a container for the payment form on your page and assign it a custom ID, as demonstrated below. Avoid setting height or display properties for elements inside the container, including the container itself, to prevent form appearance issues due to CSS conflicts.

The following example specifies a container with the default id of Solidgate SDK.

1
<div id="solid-payment-form-container"></div>

Alternatively, use your own container, passing its id to iframeParams as shown in the example below.

1
2
3
4
5
const data = {
  iframeParams: {
    containerId: 'your-custom-container-id'
  }
}   

Form initialization

After completing all the steps and modifying the required parameters, initiate the Solidgate SDK

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import React, { FC } from 'react'
import ReactDOM from 'react-dom';
import Payment, { InitConfig } from "@solidgate/react-sdk"

export const MyPayment: FC<{
  merchantData: InitConfig['merchantData']
  styles?: InitConfig['styles']
  formParams?: InitConfig['formParams']
  width?: string
}> = (props) => {
    return (<Payment 
         merchantData={props.merchantData}
         styles={props.styles}
         formParams={props.formParams}
         width={props.width}
    />)
}
1
const form = PaymentFormSdk.init(data);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
  <Payment
      :merchant-data="merchantData"
      :form-params="formParams"
      width="100%"
  />
</template>

<script lang="ts" setup>
import { defineAsyncComponent } from 'vue'
import { InitConfig } from '@solidgate/vue-sdk'
const Payment = defineAsyncComponent(() => import('@solidgate/vue-sdk'))

const merchantData: InitConfig['merchantData'] = {
  merchant: '<<--YOUR MERCHANT ID-->>',
  signature: '<<--YOUR SIGNATURE OF THE REQUEST-->>',
  paymentIntent: '<<--YOUR PAYMENT INTENT-->>'
}

const formParams: InitConfig['formParams'] = {}
</script>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/// in app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SolidPaymentModule } from '@solidgate/angular-sdk';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    SolidPaymentModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

/// in app.component.ts
import {Component} from '@angular/core';

import {FormType, InitConfig} from "@solidgate/angular-sdk";

@Component({
  selector: 'app-root',
  template: `
    <ngx-solid-payment
      [formParams]="formParams"
      [merchantData]="merchantData"
      width="100%"
    ></ngx-solid-payment>
  `
})
export class AppComponent {
  merchantData: InitConfig['merchantData'] = {
    merchant: '<<--YOUR MERCHANT ID-->>',
    signature: '<<--YOUR SIGNATURE OF THE REQUEST-->>',
    paymentIntent: '<<--YOUR PAYMENT INTENT-->>'
  }

  formParams: InitConfig['formParams'] = {
    formTypeClass: FormType.Default
  }
}

Object data (or InitConfig interface in case of frameworks usage) should contain the:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const data = {
  merchantData: {
    merchant: 'api_pk_7b197...ba108f842',
    signature: 'MjliMzU4ODE3ZDVlM2E1YWZmYzI1NmU4MzU3YjhlODRkMTJmZTk1NjIxOWNiYzFmNDk0N2NkNjk5YTA5Y2Q4NzIzOWIwMTgxZTQwOGExZjFmYWQ1NzQyYjc3ZGRjMzE0MTczYTQ2OGEyMTlmNGI4YzA5ZmNhMTczZDI0ZDBkZmM=',
    paymentIntent: 'E5FKjxw5vRjjIZBKtH_Q9oN1Wmn5icMn720prO4nPB3cYpzC9wLAHwq9IwstmD-YFLFPsdq2Rk9YzRJhxdPEq2KI19fFt1QotX-smH5_xWxGfYcv9rf2Y4v4KWgbjzJylHTDM6eCXVvbdZyVU54vD3sxntN3gFiyuhEzMn8mKoDV0UdIqLN_VsTAdehBUrqk7aPNzXCfSqpy9pCBlpdFNCfgOyHoDXGGS_Z9fK3gCw7usF2v0IU96mQGzdtyEUs1Z2MJYwle7sjEkWNEb9SkpW1zUXEZCFMF8Cu-dn6fWe4cVE2Ok1MDeTE43dySgw9e8GzMxgPmG2YFjg5xcvuedQ=='
  },
  formParams: {
    buttonType: 'default',
    submitButtonText: 'Pay',
    isCardHolderVisible: true,
    hideCvvNumbers: true,
    headerText: 'Enter your debit or credit card details (from merchant)',
    titleText: 'Card info (from merchant)',
    formTypeClass: 'default',
    googleFontLink: '//fonts.googleapis.com/css2?family=DM+Sans:ital@1&display=swap',
    autoFocus: false 
  },
  styles: {
    submit_button: {
      'background-color': 'red',
      'font-size': '16px',
      'font-weight': 'bold',
      ':hover': {
        'background-color': 'green'
      },
      form_body: {
        'font-family': 'DM Sans'
      }
    }
  }
}

Handling initialization errors

There might be errors when setting up the Payment Form and if you encounter them:

  • Verify that paymentIntent contains the required parameters.
  • Confirm that the ip_address added is not private.
  • Verify that the ip_address added matches the selected region if using a VPN.
  • Ensure that the form styles do not have the opacity property set to 0.
  • Inspect form styles for any elements that could be covering the form area.
  • Ensure that you generate a new order_id for each transaction attempt.

You can test payments to make sure everything works as expected. If the outlined steps do not resolve the issue, please contact the Solidgate support team for further assistance. When contacting, share the details of the issue and the steps you have taken to resolve it.

Form update

There is no need to call the payment form repeatedly when using multiple tariffs. Request the form once and modify the amount , currency , product ID , or any parameter of the partialIntent object using the form instance’s update method.

To update a Payment Form parameter, generate the Guide
Learn to authenticate API requests and fix validation errors effectively.
signature parameter
on your back end. This signature verifies the merchant’s request authenticity on the payment gateway server and originates from the partialIntent encrypted String

Back end setup

Firstly, ensure that the back end is prepared. In the example code, the formMerchantData function is called with fields.

Specifically, this method permits updates only to a predefined list of fields, distinct from those available during initial form creation in the paymentIntent object, updates are confined to select parameters within the partialIntent object.

It is important to note that attempting to update fields not defined in the allowed list will result in an error response.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "amount": 1020,
  "currency": "EUR",
  "order_description": "Premium package",
  "order_items": "item5",
  "order_date": "2022-02-21 11:21:30",
  "order_number": "4",    
  "type": "auth",
  "traffic_source": "Instagram",
  "transaction_source": "Main menu",
  "order_metadata": {
    "coupon_code": "NY2018",
    "partner_id": "123989"
  },
  "success_url": "http://merchant.example/success",
  "fail_url": "http://merchant.example/fail"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "product_price_id": "faf3b86a-1fe6-4ae5-84d4-ab0651d75db2",
  "currency": "EUR",
  "order_description": "Premium package",
  "order_items": "item5",
  "order_date": "2022-02-21 11:21:30",
  "order_number": "4",
  "type": "auth",
  "traffic_source": "Instagram",
  "transaction_source": "Main menu",
  "order_metadata": {
    "coupon_code": "NY2018",
    "partner_id": "123989"
  },
  "success_url": "http://merchant.example/success",
  "fail_url": "http://merchant.example/fail"
}

Step 1 Form partial intent data

For updating, provide transaction-related information. This information resides in a FormUpdateDTO object, created by invoking the formUpdate function on your API instance.

1
2
3
4
5
6
7
<?php

use SolidGate\API\Api;

$api = new Api('public_key', 'secret_key');

$formUpdateDTO = $api->formUpdate(['JSON payment intent // fill as described in documentation']);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const solidGate = require('@solidgate/node-sdk');

let api = new solidGate.Api("public_key", "secret_key");

let partialIntentData = {
    /// fill it as described in documentation
}
let formUpdateDTO = api.formUpdate(partialIntentData);

const dataToFront = formUpdateDTO.toObject()

/// This values should be applied on front end in the following way

const form.update(dataToFront)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
    "encoding/json"
    "fmt"

    solidgate "github.com/solidgate-tech/go-sdk"
)

type UpdateParams struct {
    ...
}

func main() {
    solidgateSdk := solidgate.NewSolidGateApi("public_key", "secret_key")
    partialIntent = PartialIntent{} // fill in the necessary information for updating as described in the documentation
    partialIntentBytes, err := json.Marshal(partialIntent)

    if err != nil {
        fmt.Print(err)
    }

    formUpdateDto, err := solidgateSdk.FormUpdate(partialIntentBytes)

    if err != nil {
        fmt.Print(err)
    }

    // ...  
}
1
2
3
4
5
6
7
val api = Api(HttpClient(), Credentials("public_key", "secret_key"))

val attributes = Attributes(mapOf(
    // fill as described in documentation
))

val formUpdateDTO = api.formUpdate(attributes)
1
2
3
4
5
6
7
from solidgate import ApiClient

client = ApiClient("public_key", "secret_key")

responseDTO = client.form_update({
    partial_intent_dict  // fill as described in documentation
})

Step 2 Pass generated data to front end

The FormUpdateDTO object, returned by the formUpdateDTO function, is a class instance. Convert it to a plain object for use in front-end code. This conversion is accomplished by calling the toObject function on the FormUpdateDTO object, resulting in a plain JavaScript object.

After forming the merchant data and converting it to a plain object, use it in front-end code to update with the partialIntent encrypted String

Partial form update

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { FC, useRef, useCallback, useEffect } from 'react'
import ReactDOM from 'react-dom';
import Payment, { InitConfig, ClientSdkInstance } from "@solidgate/react-sdk"

export const MyPayment: FC<{
  merchantData: InitConfig['merchantData']
  update?: {
    partialIntent: string;
    signature: string;
  }
}> = (props) => {
  const formResolve = useRef<(form: ClientSdkInstance) => void>(() => {})
  const formPromise = useRef<Promise<ClientSdkInstance>>(new Promise(resolve => {
    formResolve.current = resolve
  }))

  const handleOnReadyPaymentInstance = useCallback((form: ClientSdkInstance) => {
    formResolve.current(form)
  }, [])
  
  useEffect(() => {
    if (props.update)  {
      formPromise.then(() => form
        .update(props.update)
        .then(callbackForSuccessUpdate)
        .catch(callbackForFailedUpdate)
      )
    }
  }, [props.update])
  
  return (
    <Payment
      merchantData={props.merchantData}
      onReadyPaymentInstance={handleOnReadyPaymentInstance}
  />)
}
1
2
3
4
form
  .update({ partialIntent, signature })
  .then(callbackForSuccessUpdate)
  .catch(callbackForFailedUpdate);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
  <Payment 
    :merchant-data="merchantData"
      @ready-payment-instance="onReadyPaymentInstance"
  />
</template>

<script lang="ts" setup>
import { defineAsyncComponent } from 'vue'
import { InitConfig, ClientSdkInstance } from '@solidgate/vue-sdk'
const Payment = defineAsyncComponent(() => import('@solidgate/vue-sdk'))

const merchantData: InitConfig['merchantData'] = {
  merchant: '<<--YOUR MERCHANT ID-->>',
  signature: '<<--YOUR SIGNATURE OF THE REQUEST-->>',
  paymentIntent: '<<--YOUR PAYMENT INTENT-->>'
}

function onReadyPaymentInstance(form: ClientSdkInstance): void {
  form
      .update({ partialIntent, signature })
      .then(callbackForSuccessUpdate)
      .catch(callbackForFailedUpdate)
}
</script>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import {Component} from '@angular/core';
import {BehaviorSubject, filter} from 'rxjs'
import {InitConfig, SdkMessage, MessageType} from '@solidgate/angular-sdk';

@Component({
  selector: 'app-root',
  template: `
    <ngx-solid-payment
      [merchantData]="merchantData"
      (readyPaymentInstance)="formSubject$.next($event)"
    ></ngx-solid-payment>
  `
})
export class AppComponent {
  formSubject$ = new BehaviorSubject<ClientSdkInstance | null>(null)

  form$ = this.formSubject$.pipe(filter(Boolean))
  
  merchantData: InitConfig['merchantData'] = {
    merchant: '<<--YOUR MERCHANT ID-->>',
    signature: '<<--YOUR SIGNATURE OF THE REQUEST-->>',
    paymentIntent: '<<--YOUR PAYMENT INTENT-->>'
  }

  update(payload: {
    partialIntent: string;
    signature: string;
  }): void {
    this.form$.subscribe(form => form
      .update(payload)
      .then(callbackForSuccessUpdate)
      .catch(callbackForFailedUpdate))
  }
}
It is very important to handle possible errors, including network errors, in callbackForFailedUpdate by calling a valid update or init; otherwise, the form will remain unresponsive.

If an invalid parameter exists in the updateIntent request (e.g., a non-unique product_id), an error occurs.

1
2
3
4
5
6
7
8
{
  "error": {
    "code": "2.01",
    "message": [
      "Invalid Data"
    ]
  }
}

Coupons

Solidgate provides the ability to apply discounts to subscription products through two schemas:

  • Predefined coupon flow
    In this flow, the coupon ID is embedded and encrypted in the paymentIntent during payment initialization. Once the form is initialized, the applied discount will already be present.
  • Direct coupon flow
    In this flow, the coupon code is applied after the payment form has been initialized using a special applyCoupon method.

Predefined coupon flow

If you want to apply a discount during form initialization, you can include the coupon_id parameter in the encryption of the paymentIntent, and then call form.init

Description

ID of the coupon which corresponds to the specific product discount, must be passed in UUID v4 format.

The coupon flow will only be activated when a product_id is supplied of the subscription workflow.

Without a product_id, the coupon_id will be disregarded.

Example

eb4c6e93-4c53-447a-b215-5d5786af9844

After a successful form initialization, the discount will already be applied. However, in the case of an invalid or expired coupon, you will receive an error event with a InitPaymentError when calling form.init

Direct coupon flow

If you have already initialized the payment form and wish to apply a discount to it, you can do so by calling the applyCoupon method with the specified couponCode string.

1
2
3
4
form
  .applyCoupon("exampleCoupon")
  .then((result: ApplyCouponPrices) => { /* success handlers here */ })
  .catch((error: ApplyCouponError) => { /* error handlers here */ })

If the coupon is valid and was successfully applied, applyCoupon will resolve with ApplyCouponPrices object with schema:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
interface ApplyCouponPrices {
  productPrice: {  // initial product price
    amount: string;
    currency: string;
    currencyIcon: string;
  };
  discountPrice: { // product price with applied discount
    amount: string;
    currency: string;
    currencyIcon: string;
  };
  trialPrice?: {
    amount: string;
    currency: string;
    currencyIcon: string;
  };
}
If a trial price exists, users are charged at this rate. Future payments may also have the discountPrice applied based on rules established in the HUB. Utilize data from ApplyCouponPrices to inform users about the original and discounted product prices.

In cases where the coupon application fails due to reasons like an expired coupon code, applyCoupon rejects with an ApplyCouponError, containing error details with a specific code and message within error.details

1
2
3
4
5
6
interface ApplyCouponError extends Error {
  details?: {
    code: string
    message: string
  };
}

ApplyCouponError.details will contain original error with a specific code and message

Looking for help? Contact us
Stay informed with Changelog