Update payment form

Update payment form
Update amount, currency, and product_id with partialIntent and form.update, or replace Billing checkout line items with form.updateCheckout
Update payment form flow

Before updating parameters, complete Guide
Set up the Solidgate payment form with step-by-step instructions for script loading, container mounting, and payment request configuration.
create
your payment form so the form is initialized on the page.

An initialized form supports two update methods. Use update with partialIntent to change amount , currency , or product_id . Use updateCheckout to replace line items, discounts, or trial terms on a Billing Guide
Pass Billing-specific fields in paymentIntent for catalog products, subscriptions, and invoices.
checkout
intent. The steps below cover update . Billing checkout intents use Checkout 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
Authenticate with the Solidgate API using merchant credentials, configure request signing, and start processing live payment transactions.
signature
parameter on your backend. This signature verifies the merchant’s request authenticity on the payment gateway server and originates from the partialIntent encrypted String

Backend setup

Firstly, ensure that the backend is prepared. In the example code, the formUpdate function is called with fields.

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

It is important to note that attempting to update fields not defined in the allowed list results in an error response.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
  "amount": 1020,
  "currency": "EUR",
  "order_description": "Premium package",
  "order_items": "item5",
  "order_date": "2025-02-21 11:21:30",
  "order_number": 4,
  "settle_interval": 48,
  "force3ds": true,
  "customer_email": "user-one@mail.com",
  "traffic_source": "Instagram",
  "transaction_source": "Main menu",
  "order_metadata": {
    "coupon_code": "NY2025",
    "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
18
19
20
{
  "product_id": "47f95c95-3647-4c5b-ae6d-40fd8d3ac742",
  "customer_account_id": "4dad42f808",
  "currency": "EUR",
  "order_description": "Premium package",
  "order_items": "item5",
  "order_date": "2025-02-21 11:21:30",
  "order_number": 4,
  "settle_interval": 48,
  "force3ds": true,
  "customer_email": "user-one@mail.com",
  "traffic_source": "Instagram",
  "transaction_source": "Main menu",
  "order_metadata": {
    "coupon_code": "NY2025",
    "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
18
19
20
{
  "product_id": "faf3b86a-1fe6-4ae5-84d4-ab0651d75db2",
  "customer_account_id": "4dad42f808",
  "currency": "EUR",
  "order_description": "Premium package",
  "order_items": "item5",
  "order_date": "2025-02-21 11:21:30",
  "order_number": 4,
  "settle_interval": 48,
  "force3ds": true,
  "customer_email": "user-one@mail.com",
  "traffic_source": "Instagram",
  "transaction_source": "Main menu",
  "order_metadata": {
    "coupon_code": "NY2025",
    "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 := solidgate.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
from solidgate import ApiClient

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

partial_intent_dict = {} # fill as described in documentation
responseDTO = client.form_update(partial_intent_dict)
 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
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class FormUpdateGenerator {
    private final String publicKey = "api_pk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";
    private final String secretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";

    public String encryptPartialIntent(String jsonString) 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[] encrypted = cipher.doFinal(jsonString.getBytes(StandardCharsets.UTF_8));
        byte[] withIV = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, withIV, 0, iv.length);
        System.arraycopy(encrypted, 0, withIV, iv.length, encrypted.length);
        return Base64.getEncoder().encodeToString(withIV).replace("+", "-").replace("/", "_");
    }

    public String generateSignature(String jsonString) throws Exception {
        String data = publicKey + jsonString + publicKey;
        Mac mac = Mac.getInstance("HmacSHA512");
        mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA512"));
        byte[] hashBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder hexHash = new StringBuilder();
        for (byte b : hashBytes) hexHash.append(String.format("%02x", b & 0xff));
        return Base64.getEncoder().encodeToString(hexHash.toString().getBytes(StandardCharsets.UTF_8));
    }

    public static void main(String[] args) throws Exception {
        FormUpdateGenerator gen = new FormUpdateGenerator();
        String partialIntentJson = "{\"amount\":1020,\"currency\":\"EUR\"}"; // fill as described in documentation
        String partialIntent = gen.encryptPartialIntent(partialIntentJson);
        String signature = gen.generateSignature(partialIntentJson);
        // Pass partialIntent and signature to form.update() on the frontend
        System.out.println("partialIntent: " + partialIntent);
        System.out.println("signature: " + signature);
    }
}
 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
require 'openssl'
require 'base64'

class FormUpdateGenerator
  KEY_LENGTH = 32
  IV_LENGTH  = 16

  def initialize
    @public_key = 'api_pk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx'
    @secret_key = 'api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx'
  end

  def encrypt_partial_intent(json_string)
    key    = @secret_key[0, KEY_LENGTH]
    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(json_string) + cipher.final
    Base64.urlsafe_encode64(iv + encrypted).gsub('+', '-').gsub('/', '_')
  end

  def generate_signature(json_string)
    data   = @public_key + json_string + @public_key
    digest = OpenSSL::Digest.new('sha512')
    hmac   = OpenSSL::HMAC.hexdigest(digest, @secret_key, data)
    Base64.strict_encode64(hmac)
  end
end

gen                = FormUpdateGenerator.new
partial_intent_json = '{"amount":1020,"currency":"EUR"}' # fill as described in documentation

partial_intent = gen.encrypt_partial_intent(partial_intent_json)
signature      = gen.generate_signature(partial_intent_json)

# Pass partial_intent and signature to form.update() on the frontend
puts "partialIntent: #{partial_intent}"
puts "signature: #{signature}"
 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
using System;
using System.Security.Cryptography;
using System.Text;

class FormUpdateGenerator
{
    private const string PublicKey = "api_pk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";
    private const string SecretKey = "api_sk_xxxxaxxxxbdf47a2aea17eb3xxxxxxxx";

    public static string EncryptPartialIntent(string jsonString)
    {
        byte[] iv = new byte[16];
        RandomNumberGenerator.Fill(iv);
        using (var aes = new RijndaelManaged())
        {
            aes.Key     = Encoding.UTF8.GetBytes(SecretKey.Substring(0, 32));
            aes.Mode    = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            using (var encryptor = aes.CreateEncryptor(aes.Key, iv))
            {
                byte[] valueBytes = Encoding.UTF8.GetBytes(jsonString);
                byte[] encrypted  = encryptor.TransformFinalBlock(valueBytes, 0, valueBytes.Length);
                byte[] withIV     = new byte[iv.Length + encrypted.Length];
                Array.Copy(iv,        0, withIV, 0,         iv.Length);
                Array.Copy(encrypted, 0, withIV, iv.Length, encrypted.Length);
                return Convert.ToBase64String(withIV).Replace("+", "-").Replace("/", "_");
            }
        }
    }

    public static string GenerateSignature(string jsonString)
    {
        string data = PublicKey + jsonString + PublicKey;
        using (var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(SecretKey)))
        {
            byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
            string hexHash   = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(hexHash));
        }
    }

    static void Main()
    {
        string partialIntentJson = "{\"amount\":1020,\"currency\":\"EUR\"}"; // fill as described in documentation
        string partialIntent     = EncryptPartialIntent(partialIntentJson);
        string signature         = GenerateSignature(partialIntentJson);
        // Pass partialIntent and signature to form.update() on the frontend
        Console.WriteLine("partialIntent: " + partialIntent);
        Console.WriteLine("signature: "     + signature);
    }
}

Step 2. Pass generated data to frontend

The FormUpdateDTO object, returned by the FormUpdateDTO function, is a class instance. Convert it to a plain object for use in frontend 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 frontend 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
37
38
39
40
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>>()

  const handleOnReadyPaymentInstance = useCallback((form: ClientSdkInstance) => {
    formResolve.current(form)
  }, [])

  useEffect(() => {
    formPromise.current = new Promise<ClientSdkInstance>((resolve) => {
      formResolve.current = resolve
    })
  }, [])

  useEffect(() => {
    if (props.update && formPromise.current) {
      formPromise.current.then((form) => 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 remains unresponsive.

If an invalid parameter exists in the updateIntent request, such as a non-unique product_id, an error occurs.

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

A Billing checkout intent does not update plan, quantity, coupon, or trial through partialIntent and update . Those fields live on the checkout object at init, so the form exposes a separate updateCheckout method instead of the signed formUpdate flow above.

Checkout update

updateCheckout changes line items, discounts, or trial terms on a form initialized with a Billing checkout object. It applies to Guide
Configure API access, create products and customers, set up recurring billing, handle webhook events, and manage cancellations with Solidgate.
Subscription 2.0
and Guide
Configure API access, create products and customers, issue invoices, process payments, and handle webhook events to integrate Solidgate invoicing.
Invoice
intents that include checkout.mode as subscription or invoice .

The form instance uses camelCase field names. The encrypted Guide
Pass Billing-specific fields in paymentIntent for catalog products, subscriptions, and invoices.
paymentIntent
uses snake_case for the same checkout data.

Prerequisites

updateCheckout works only on a payment intent created in checkout flow, with a checkout block and mode invoice or subscription .

A one-time payment intent, or an intent paid with product_price_id or invoice_id and no checkout block, fails with Not a checkout flow.

Method call

Call updateCheckout on the form instance returned from PaymentFormSdk.init. Do not send partialIntent or a backend formUpdate signature.

The method always settles the promise it returns. It resolves with the updated invoice preview on success, or rejects with an Error on failure. It does not throw synchronously. Wrap the await call in try/catch.

 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
import React, { FC, useRef, useCallback, useEffect } from 'react'
import Payment, { InitConfig, ClientSdkInstance } from "@solidgate/react-sdk"

export const MyPayment: FC<{
  merchantData: InitConfig['merchantData']
  checkoutConfig?: {
    lineItems: Array<{ productPriceId: string; quantity: number; description?: string }>
    discounts?: Array<{ couponId?: string; couponCode?: string }>
  }
}> = (props) => {
  const formResolve = useRef<(form: ClientSdkInstance) => void>(() => {})
  const formPromise = useRef<Promise<ClientSdkInstance>>()

  const handleOnReadyPaymentInstance = useCallback((form: ClientSdkInstance) => {
    formResolve.current(form)
  }, [])

  useEffect(() => {
    formPromise.current = new Promise<ClientSdkInstance>((resolve) => {
      formResolve.current = resolve
    })
  }, [])

  useEffect(() => {
    if (props.checkoutConfig && formPromise.current) {
      formPromise.current.then((form) => form
        .updateCheckout(props.checkoutConfig)
        .then((result) => { /* use result.invoicePreview */ })
        .catch((error) => { /* handle UpdateCheckoutError */ })
      )
    }
  }, [props.checkoutConfig])

  return (
    <Payment
      merchantData={props.merchantData}
      onReadyPaymentInstance={handleOnReadyPaymentInstance}
  />)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const form = PaymentFormSdk.init({
  merchantData: { merchant, signature, paymentIntent },
});

try {
  const result = await form.updateCheckout({
    lineItems: [{ productPriceId: "b1e6c2b0-3a4d-4f2e-9b7a-1234567890ab", quantity: 2 }],
    discounts: [{ couponCode: "SUMMER10" }],
  });

  // Success: result.invoicePreview holds the recalculated totals
  console.log(result.invoicePreview.total, result.invoicePreview.currency);
} catch (error) {
  console.error(error);
}
 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
<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-->>'
}

async function onReadyPaymentInstance(form: ClientSdkInstance): Promise<void> {
  try {
    const result = await form.updateCheckout({
      lineItems: [{ productPriceId: 'b1e6c2b0-3a4d-4f2e-9b7a-1234567890ab', quantity: 2 }],
      discounts: [{ couponCode: 'SUMMER10' }],
    })
    // use result.invoicePreview
  } catch (error) {
    // handle UpdateCheckoutError
  }
}
</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} 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-->>'
  }

  updateCheckout(checkoutConfig: {
    lineItems: Array<{ productPriceId: string; quantity: number }>;
    discounts?: Array<{ couponCode?: string; couponId?: string }>;
  }): void {
    this.form$.subscribe(form => form
      .updateCheckout(checkoutConfig)
      .then((result) => { /* use result.invoicePreview */ })
      .catch((error) => { /* handle UpdateCheckoutError */ }))
  }
}

Input shape

 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
interface UpdateCheckoutConfig {
  lineItems: UpdateCheckoutLineItem[];
  discounts?: UpdateCheckoutDiscount[];
  subscriptionData?: UpdateCheckoutSubscriptionData;
}

interface UpdateCheckoutLineItem {
  productPriceId: string;
  quantity: number;
  description?: string;
}

interface UpdateCheckoutDiscount {
  couponId?: string;
  couponCode?: string;
}

interface UpdateCheckoutSubscriptionData {
  trial?: {
    type: "free" | "paid";
    period: { value: number; unit: "day" | "week" | "month" };
    amount?: number;
    settleInterval?: number;
  };
  metadata?: Record<string, string>;
  description?: string;
}

Output shape

A successful call resolves with invoicePreview. Amounts are decimal strings, for example “40.00”, not floats, to avoid precision loss.

 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
interface UpdateCheckoutResult {
  invoicePreview: CheckoutInvoicePreview;
}

interface CheckoutInvoicePreview {
  total: string;
  currency: string;
  currencyIcon: string;
  amounts?: {
    subtotal: string;
    discount: string;
    unitPrice?: string;
    taxable?: string;
    tax?: string;
  };
  lineItems: Array<{
    amount: string;
    quantity: number;
    currency: string;
    currencyIcon: string;
    productPriceId: string;
    productId: string;
    amounts?: CheckoutInvoicePreview['amounts'];
    tax?: { categoryId: string; mode: string; rate: number };
  }>;
}

Error handling

When updateCheckout rejects, the caught value is an Error named UpdateCheckoutError. The Error message is a human-readable summary and includes the details payload.

1
2
3
4
5
6
7
8
interface UpdateCheckoutError extends Error {
  name: "UpdateCheckoutError";
  message: string;
  details: {
    code: string;
    message: string[] | Record<string, string>;
  };
}

details.message takes one of two shapes, depending on the kind of failure:

  • An array of strings for a request-level failure, for example ["Intent is not payable"] or ["Init payment not found"]. Render these as a general error banner.
  • An object mapping field to message for input that failed validation, for example { "lineItems[0].productPriceId": "must be a valid UUID" }. Render these as per-field hints.

Check which shape details.message is before using it.


Looking for help? Contact us
Stay informed with Changelog