Before updating parameters, complete 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
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
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 thepaymentIntent 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. Choose your payment scenario to see the fields it requires. Payment amount integer >=0 Required
Description
Order amount in minor units. For example, 1020 means 10 USD and 20 cents. Can be 0 for zero-amount authorization.
Example
1020
Description
Identifier of the predefined product in UUID v4 format.
Example
faf3b86a-1fe6-4ae5-84d4-ab0651d75db2
Description
Customer ID in the merchant’s system.
Example
4dad42f878
Description
Identifier of the predefined product in UUID v4 format.
Example
faf3b86a-1fe6-4ae5-84d4-ab0651d75db2
Description
Customer ID in the merchant’s system.
Example
4dad42f878
Description
Currency in three-letter code per the ISO-4217 Wiki standard.
Example
USD
Description
Order description in your system and for bank processing.
Highly recommended to keep the description brief to improve the clarity of payment processing, ideally not exceeding 100 characters. It is used in the email receipt sent to the customer.Example
Premium package
Description
Order items in UTF-8 code.
Example
item1, item2
Description
Date of order creation following the ^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$ pattern.
Example
2025-12-21 11:21:30
Description
Number of payments by the customer.
Example
1
Description
Delay before settlement in hours:
- Visa customer-initiated payments: 240 hours = 10 days
- Visa merchant-initiated payments: 120 hours = 5 days
- All other card brands: 168 hours = 7 days
Example
48
Description
Routing payments flag for 3DS flow.
Example
true
Description
Customer email.
If email on the initialization payment form was:
- non-empty, you cannot change it to an empty email
- non-empty, and you provide a new email, the new value is passed
- empty and you provide a new email, the form uses that email as a prefilled value
The customer has the option to change it or to confirm it by submission.
Example
traffic_source string 255Description
Identifies the marketing or acquisition channel that brought the customer to the transaction.
Example
facebook
Description
Identifies the internal system or flow that triggered the transaction.
Example
main_menu
Description
Metadata is useful for storing additional, structured information about an object, consisting of up to 10 key-value pairs with a validation limit of 380 characters per field.
The callback notification returns an order_metadata from the order in each state.
Example
{"coupon_code": "NY2025", "partner_id": "123989"}
Description
Provide this URL if you want to redirect a customer to your own Success Screen.
If you do not provide the URL, Solidgate directs customers to the Solidgate Success Screen. The Solidgate notification screen is not customizable, but you can define your own success and fail pages during Payment Form initialization withsuccess_url and fail_url.
Example
http://merchant.example/success
Description
Provide this URL if you want to redirect a customer to your own Fail Screen.
If you do not provide the URL, Solidgate directs customers to the Solidgate Fail Screen.Example
http://merchant.example/fail
{
"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"
}
Product-based
{
"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"
}
Subscription 1.0
{
"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.
<?php
use SolidGate\API\Api;
$api = new Api('public_key', 'secret_key');
$formUpdateDTO = $api->formUpdate(['JSON payment intent // fill as described in documentation']);
Node.js
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)
Go
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)
}
// ...
}
Kotlin
val api = Api(HttpClient(), Credentials("public_key", "secret_key"))
val attributes = Attributes(mapOf(
// fill as described in documentation
))
val formUpdateDTO = api.formUpdate(attributes)
Python
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)
Java
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);
}
}
Ruby
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}"
C#
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
partialIntent stringDescription
Encrypted aes-cbc-256 string of JSON request data with random IV (16 bytes) and secret key is the first 32 bytes of the merchant secret key.
Example
E5FKjxw5vRjjIZ....vmG2YFjg5xcvuedQ==
Description
Signature of request.
It allows verifying whether the request from the Merchant is genuine on the payment gateway server.
Example
MjNiYjVj…ZhYmMxMzNiZDY=
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}
/>)
}
JavaScript
form
.update({ partialIntent, signature })
.then(callbackForSuccessUpdate)
.catch(callbackForFailedUpdate);
Vue
<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>
Angular
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.
{
"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
Subscription 2.0
and
Invoice
intents that include checkout.mode as
subscription
or
invoice
.
The form instance uses camelCase field names. The encrypted 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
.
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.
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}
/>)
}
JavaScript
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);
}
Vue
<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>
Angular
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
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;
}
lineItems
array of objects
Required
Description
Full replacement set of line items for the checkout.
Must contain at least one item.productPriceId string Required
Description
Product price ID charged on this line. The ID must resolve to a product and price known to the merchant.
Example
b1e6c2b0-3a4d-4f2e-9b7a-1234567890ab
Description
Quantity for the line item. A positive whole number.
Example
2
Description
Free-text description for the line item, shown on the invoice.
Example
Annual plan seats
Description
Coupon applied to the checkout. Pass at most one discount. For a coupon on a checkout intent, pass discounts here rather than the applyCoupon method.
couponId or couponCode not both.
couponId string [1..100]
Description
Internal coupon identifier.
Cannot be used together withcouponCode.
Example
coup_abc123
Description
Customer-facing coupon code. Letters and digits only.
Cannot be used together withcouponId.
Example
SUMMER10
Description
Subscription configuration. Use only when checkout.mode is
subscription
.
trial object
Description
Trial terms for the subscription.
type string Required
Description
Whether the trial is free or a paid trial.
Allowed values arefree and paid. Required when trial is present.
Example
paid
Description
Length of the trial. Required when trial is present.
day ≤ 365, week ≤ 52, and month ≤ 12.
value integer Required
Description
Trial duration in units.
Example
7
Description
Trial duration unit.
Allowed values areday, week, and month.
Example
day
Description
Trial charge amount. A positive number.
Required whentype is paid. Omit when type is free.
Example
199
Description
Delay before automatic settlement of the paid trial charge, in hours.
Required whentype is paid. Omit when type is free.
Example
48
Description
Free-form key/value metadata attached to the subscription.
At most 10 pairs.Example
{
"campaign": "spring_2026",
"channel": "ads"
}
description
string
[1..500]
Description
Free-text description for the subscription.
Example
Premium plan annual
Output shape
A successful call resolves with invoicePreview. Amounts are decimal strings, for example “40.00”, not floats, to avoid precision loss.
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 };
}>;
}
invoicePreview.total
string
Description
Total amount due, as a decimal string.
Example
40.00
Description
ISO currency code.
Example
USD
Description
Currency symbol for display.
Example
$
Description
Amount breakdown. All values are decimal strings.
subtotal string
Description
Subtotal before discount and tax.
Example
50.00
Description
Total discount applied.
Example
10.00
Description
Unit price, when applicable.
Example
25.00
Description
Taxable amount, when applicable.
Example
40.00
Description
Tax amount, when applicable.
Example
0.00
Description
Per-line-item breakdown. Mirrors the request line items with computed amounts. Currency on each line matches the overall invoice currency.
amount string
Description
Line total.
Example
40.00
Description
Quantity.
Example
2
Description
ISO currency code for this line.
Example
USD
Description
Currency symbol for this line.
Example
$
Description
Price identifier echoing the request.
Example
b1e6c2b0-3a4d-4f2e-9b7a-1234567890ab
Description
Product identifier from the catalog.
Example
fa43b415-5522-4373-b026-a365562f9649
Description
Same shape as the top-level amounts, scoped to this line.
Example
{
"subtotal": "50.00",
"discount": "10.00"
}
tax
object
Description
Tax classification and rate applied to this line. Includes categoryId, mode, and rate.
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.
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.