Skip to main content

Overview

A Payment Intent represents a customer's intent to make a payment. It tracks the entire payment lifecycle from creation to completion, handling all the complexity of cryptocurrency payments behind a simple API.

What is a Payment Intent?

A Payment Intent is a server-side object that:

  • 💰 Stores payment amount and currency (USD)
  • 🔄 Tracks payment status through its lifecycle
  • 👤 Links to customer information
  • 🏷️ Holds metadata for order tracking
  • 💳 Accepts USDC and USDT as payment methods

💡 Key Concept: Think of a Payment Intent as a "payment request" that persists on the server. Once created, you guide your customer through the payment process, and the Payment Intent automatically updates as the payment progresses.

How It Works

Create Intent → Customer Pays → Payment Confirms → Funds Settled
↓ ↓ ↓ ↓
pending → requires_action → processing → succeeded

Key Benefits

BenefitDescription
Unified InterfaceConsistent API across all payment scenarios
Status TrackingReal-time payment status updates
Automatic RetriesBuilt-in handling for failed payments
Webhook SupportNotifications for every status change
Currency FlexibilityAccept USDC/USDT while pricing in USD
Metadata SupportAttach custom data for order tracking

Creation Methods

MartianPay offers three ways to create Payment Intents:

MethodBest ForIntegration Effort
Direct APICustom checkout flowsHigh (full control)
Payment LinksQuick setup, no codingLow (hosted page)
Client SDKWeb/mobile appsMedium (pre-built UI)

💡 Getting Started: New to payments? Start with Payment Links for the quickest setup. Need custom integration? Use Direct API below.


API Reference

1. Create Payment Intent

Creates a new payment intent for processing a cryptocurrency transaction.

📌 Note: Currently only USD is supported as the payment intent currency. Customers can pay using USDC or USDT stablecoins.

Method 1: Direct API Call ⭐ Most Flexible

Best For: Custom checkout experiences, full control over payment flow

For detailed API reference, see: Create Payment Intent API

Request Parameters:

  • amount (required): Amount to be charged in the specified currency
  • currency (required): ISO 4217 currency code (currently only "USD" is supported)
  • customer (optional): Customer ID this payment intent belongs to
  • description (optional): Description of the payment intent
  • metadata (optional): Key-value pairs for additional information
  • merchant_order_id (required): Your unique order identifier
  • receipt_email (optional): Email address to send the receipt to
  • return_url (optional): URL to redirect the customer after payment
  • payment_method_id (optional): ID of a saved payment method (Stripe only)
  • complete_on_first_payment (optional): Complete payment immediately when first transaction arrives, regardless of amount

Request:

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"amount": "10.00",
"currency": "USD",
"customer": "cus_7p51tXM7GBi7sz5J",
"description": "Payment for Order #123",
"metadata": {
"order_id": "order_123",
"customer_name": "John Doe"
},
"merchant_order_id": "order_1234"
}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USD",
"amount": "0"
},
"amount_refunded": {
"asset_id": "USD",
"amount": "0"
},
"tx_fee": {
"asset_id": "USD",
"amount": "0"
},
"tax_fee": {
"asset_id": "USD",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USD",
"amount": "0"
},
"net_amount": {
"asset_id": "USD",
"amount": "0"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USD",
"amount": "0"
}
},
"canceled_at": 0,
"cancellation_reason": "",
"client_secret": "pi_vfk7mLTHPs7Cf0UbU38tGiKq_secret_QN30gkdjG1BQuFIqKgObDhXHl",
"created": 1748575946,
"updated": 1748575946,
"currency": "USD",
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"object": "customer",
"total_expense": 490,
"total_payment": 580,
"total_refund": 90,
"currency": "USD",
"created": 1737964162,
"name": "John Doe",
"email": "john@example.com",
"description": "VIP customer",
"metadata": {
"source": "web",
"user_id": "123"
},
"phone": "+1234567890"
},
"description": "Payment for Order #123",
"livemode": false,
"metadata": {
"customer_name": "John Doe",
"order_id": "order_123"
},
"payment_link_details": null,
"merchant_order_id": "order_1234",
"payment_method_type": "",
"charges": [],
"receipt_email": "",
"return_url": "",
"status": "RequiresPaymentMethod",
"payment_intent_status": "Created",
"complete_on_first_payment": false,
"permanent_deposit": false,
"permanent_deposit_asset_id": "",
"expired_at": 0
}
}

For simple payment scenarios, you can use payment links created for products. Customers select products/variants and complete payment through a hosted checkout page. A payment intent is automatically created when the customer initiates checkout.

For detailed information on creating payment links, see: How to Use Payment Links

Workflow:

  1. Create a product with variants (if needed)
  2. Create a payment link for the product
  3. Share the payment link URL with customers
  4. Customer selects options and completes payment
  5. Payment intent is automatically created upon checkout initiation
  6. Track payment status via webhooks or API

Payment Link URL Format:

https://buy.martianpay.com/{payment_link_id}

Benefits:

  • No backend integration required for payment flow
  • Hosted checkout UI provided by MartianPay
  • Automatic payment intent creation
  • Support for product variants and add-ons
  • Mobile-optimized payment experience

For seamless integration with external authentication systems (Instagram, WhatsApp, your own platform), you can use ephemeral tokens to authenticate customers automatically without requiring them to log in again.

For detailed implementation guide, see: Payment Link with Ephemeral Token

Step 1: Generate Ephemeral Token

curl --location --request POST 'https://api.martianpay.com/v1/customers/ephemeral_tokens' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data '{
"allow_create": true,
"channel_metadata": {
"platform_user_id": "user_12345",
"source": "instagram_dm"
},
"idp_key": "email",
"idp_subject": "customer@example.com",
"issued_by": "instagram_commerce_bot",
"provider": "instagram",
"order_id": "order_123",
"return_url": "https://example.com/payment/success"
}'

Response:

{
"expires_at": 1735689600,
"token": "eph_abc123xyz789"
}

Step 2: Share Payment Link with Token

Append the ephemeral token to your payment link URL:

https://buy.martianpay.com/{payment_link_id}?ephemeral_token=eph_abc123xyz789

Step 3: Customer Completes Payment

When the customer clicks the link:

  1. They're automatically authenticated (no login required)
  2. They see the product and select options
  3. They complete payment through the hosted checkout
  4. Payment intent is automatically created upon checkout initiation

Step 4: Track Payment Status

After successful payment, you have two ways to track the created payment intent:

  1. Via Webhook (Recommended):

    • Listen for payment_intent.succeeded webhook event
    • Extract merchant_order_id from the payment intent payload
    • Use this merchant_order_id to map the payment to your order system
  2. Via Get Payment Intent API:

    • Query payment intent using the merchant_order_id
    • Example:
    curl --location --request GET 'https://api.martianpay.com/v1/payment_intents?merchant_order_id={order_id}' \
    --header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'
    • This returns the payment intent associated with the specified order ID
    • Useful for reconciliation and status checking after payment

Benefits:

  • No additional login step - seamless customer experience
  • Works with any external identity provider (email, phone, or UUID-based)
  • Supports anonymous tokens for order-only tracking (without customer identity)
  • Secure with short-lived tokens (5-15 minutes)
  • Auto-creates new customers if they don't exist
  • Tracks platform-specific data through channel_metadata

Use Cases:

  • Instagram Commerce: Send payment links in DMs with auto-authentication
  • WhatsApp Business: Share payment links without customer login
  • Telegram Bots: Offer one-click checkout
  • Custom Platforms: Integrate MartianPay into your existing user system

Understanding the Response

Success Check: error_code: "success" ✅ Payment intent created

Key Fields:

  • id: Payment intent identifier (use for updates/queries)
  • client_secret: Use in frontend for checkout (MartianPay JS SDK)
  • status: Payment status (Created, Processing, Completed, etc.)
  • payment_url: Hosted checkout page URL

Next Steps:

  • Use client_secret with MartianPay JS SDK for embedded checkout
  • OR share payment_url for hosted checkout
  • Poll status or use webhooks to monitor payment completion

2. Update Payment Intent

Modifies an existing payment intent, allowing changes to its properties. You can update the payment method and specify crypto or fiat payment options.

Integration Methods:

  • Using MartianPay JS SDK (@martianpay/js): If you're integrating with our JavaScript SDK, you do not need to call this API directly. The SDK handles payment method updates automatically. See MartianPay JS Usage Guide for details.
  • Custom Web Implementation: If you're building your own custom payment interface without the SDK, you need to call this API to update payment methods. See Custom Payment Interface Guide for implementation details.

For detailed API reference, see:

Payment Method Options:

  • Crypto: Specify the asset_id for cryptocurrency payments (e.g., "USDC-Ethereum-TEST"). Asset IDs can be obtained from the List Assets API.
  • Fiat: Specify the currency for fiat payments (e.g., "USD"). Optionally include payment_method_id for saved payment methods (Stripe) and save_payment_method to save the card for future use.

Request Example (Crypto):

Backend API (using secret key):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/:id' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"payment_method_type": "crypto",
"payment_method_options": {
"crypto": {
"asset_id": "USDC-Ethereum-TEST"
}
}
}'

Frontend API (using public key, for custom payment interface):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/link/:id/update' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_method_type": "crypto",
"payment_method_options": {
"crypto": {
"asset_id": "USDC-Ethereum-TEST"
}
},
"key": "public_key",
"client_secret": "client_secret"
}'

Request Example (Fiat with new card):

Backend API (using secret key):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/:id' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"payment_method_type": "cards",
"payment_method_options": {
"fiat": {
"currency": "USD",
"save_payment_method": true
}
}
}'

Frontend API (using public key, for custom payment interface):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/link/:id/update' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_method_type": "cards",
"payment_method_options": {
"fiat": {
"currency": "USD",
"save_payment_method": true
}
},
"key": "public_key",
"client_secret": "client_secret"
}'

Request Example (Fiat with saved payment method):

Before using a saved payment method, you need to fetch the customer's saved payment methods using the List Payment Methods API and let the user select one.

Backend API (using secret key):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/:id' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"payment_method_type": "cards",
"payment_method_options": {
"fiat": {
"currency": "USD",
"payment_method_id": "pm_1234567890"
}
}
}'

Frontend API (using public key, for custom payment interface):

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/link/:id/update' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_method_type": "cards",
"payment_method_options": {
"fiat": {
"currency": "USD",
"payment_method_id": "pm_1234567890"
}
},
"key": "public_key",
"client_secret": "client_secret"
}'

Note: The payment_method_id should be obtained from the customer's saved payment methods list. See the Payment Methods API documentation for details on how to retrieve saved payment methods.

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USD",
"amount": "0"
},
"amount_refunded": {
"asset_id": "USD",
"amount": "0"
},
"tx_fee": {
"asset_id": "USD",
"amount": "0"
},
"tax_fee": {
"asset_id": "USD",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USD",
"amount": "0"
},
"net_amount": {
"asset_id": "USD",
"amount": "0"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USD",
"amount": "0"
}
},
"canceled_at": 0,
"cancellation_reason": "",
"client_secret": "pi_vfk7mLTHPs7Cf0UbU38tGiKq_secret_QN30gkdjG1BQuFIqKgObDhXHl",
"created": 1748575946,
"updated": 1748575946,
"currency": "USD",
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"object": "customer",
"total_expense": 490,
"total_payment": 580,
"total_refund": 90,
"currency": "USD",
"created": 1737964162,
"name": "John Doe",
"email": "john@example.com",
"description": "VIP customer",
"metadata": {
"source": "web",
"user_id": "123"
},
"phone": "+1234567890"
},
"description": "Payment for Order #123",
"livemode": false,
"metadata": {
"customer_name": "John Doe",
"order_id": "order_123"
},
"payment_link_details": null,
"merchant_order_id": "order_1234",
"payment_method_type": "crypto",
"charges": [
{
"id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"object": "charge",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"amount_refunded": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"tx_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"tax_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"net_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
}
},
"exchange_rate": "1.0",
"calculated_statement_descriptor": "",
"captured": false,
"created": 0,
"customer": "",
"description": "",
"disputed": false,
"failure_code": "",
"failure_message": "",
"fraud_details": null,
"livemode": false,
"metadata": null,
"paid": false,
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"payment_method_type": "crypto",
"payment_method_options": {
"crypto": {
"amount": "10000000",
"token": "USDC",
"asset_id": "USDC-Ethereum-TEST",
"network": "Ethereum Sepolia",
"decimals": 6,
"exchange_rate": "1.0",
"deposit_address": "0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f",
"expired_at": 1748577793
}
},
"transactions": [],
"receipt_email": "",
"return_url": "",
"receipt_url": "",
"refunded": false,
"refunds": [],
"review": null,
"payer_max_payload": null,
"stripe_payload": null,
"payment_provider": ""
}
],
"receipt_email": "",
"return_url": "",
"status": "Processing",
"payment_intent_status": "Waiting",
"complete_on_first_payment": false,
"permanent_deposit": false,
"permanent_deposit_asset_id": "",
"expired_at": 0
}
}

Understanding the Response

Success Check: error_code: "success" ✅ Payment intent updated

What You Can Update:

  • Payment method (crypto/fiat selection)
  • Payment method details

Common Updates: Select crypto (USDC, USDT) or fiat (card) payment method.

Next Steps: Customer completes payment via selected method, monitor status via webhooks.

3. Customer Pay the Payment Intent

To complete the payment, customers can choose between crypto and fiat payment methods:

Crypto Payment

For cryptocurrency payments, the customer should follow these steps:

  1. Open their preferred cryptocurrency wallet that supports the selected asset (e.g., USDC on Ethereum Sepolia network)
  2. Initiate a transaction to send the required amount to the provided deposit address Example: 0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f
  3. Ensure the transaction is sent before the expiration time (check expired_at field)
  4. Wait for the transaction to be confirmed on the blockchain network

Fiat Payment (Card Payment)

For fiat card payments, there are two integration options:

Option 1: Using MartianPay JS SDK (Recommended)

  • The SDK automatically handles the payment UI and flow
  • Customers enter card details in a secure, pre-built interface
  • Supports saving cards for future payments
  • See MartianPay JS Usage Guide for implementation

Option 2: Custom Integration

  • Build your own payment interface using Stripe.js
  • Load Stripe Elements and handle card input
  • See Custom Payment Interface Guide for detailed implementation

Card payment features:

  • Support for saved payment methods (returning customers)
  • PCI-compliant card handling through Stripe
  • Real-time payment confirmation
  • Optional card saving for future use

Payment Status Tracking

Once the payment is successfully processed, the payment intent status will be updated accordingly.

MartianPay sends webhook notifications for all payment intent status changes, including:

  • payment_intent.created - Payment intent created
  • payment_intent.processing - Payment processing started
  • payment_intent.succeeded - Payment completed successfully
  • payment_intent.payment_failed - Payment failed
  • payment_intent.partially_paid - Partial payment received (crypto only)
  • payment_intent.canceled - Payment canceled

These webhook events allow you to track the payment lifecycle in real-time and automate order fulfillment. For detailed webhook setup, event schemas, and best practices, see Section 7: Webhook Events.

4. Get Payment Intent

Retrieves details of a specific payment intent.

For detailed API reference, see: Get Payment Intent API

Request:

curl --location --request GET 'https://api.martianpay.com/v1/payment_intents/:id' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USD",
"amount": "10"
},
"amount_refunded": {
"asset_id": "USD",
"amount": "0"
},
"tx_fee": {
"asset_id": "USD",
"amount": "0.1"
},
"tax_fee": {
"asset_id": "USD",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USD",
"amount": "0"
},
"net_amount": {
"asset_id": "USD",
"amount": "9.9"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USD",
"amount": "0"
}
},
"canceled_at": 0,
"cancellation_reason": "",
"client_secret": "pi_vfk7mLTHPs7Cf0UbU38tGiKq_secret_QN30gkdjG1BQuFIqKgObDhXHl",
"created": 1748575946,
"updated": 1748576345,
"currency": "USD",
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"object": "customer",
"total_expense": 490,
"total_payment": 580,
"total_refund": 90,
"currency": "USD",
"created": 1737964162,
"name": "John Doe",
"email": "john@example.com",
"description": "VIP customer",
"metadata": {
"source": "web",
"user_id": "123"
},
"phone": "+1234567890"
},
"description": "Payment for Order #123",
"livemode": false,
"metadata": {
"customer_name": "John Doe",
"order_id": "order_123"
},
"payment_link_details": null,
"merchant_order_id": "order_1234",
"payment_method_type": "crypto",
"charges": [
{
"id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"object": "charge",
"amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "10"
},
"amount_refunded": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"tx_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0.1"
},
"tax_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
},
"net_amount": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "9.9"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USDC-Ethereum-TEST",
"amount": "0"
}
},
"exchange_rate": "1.0",
"calculated_statement_descriptor": "",
"captured": false,
"created": 0,
"customer": "",
"description": "",
"disputed": false,
"failure_code": "",
"failure_message": "",
"fraud_details": null,
"livemode": false,
"metadata": null,
"paid": false,
"payment_intent": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"payment_method_type": "crypto",
"payment_method_options": {
"crypto": {
"amount": "10000000",
"token": "USDC",
"asset_id": "USDC-Ethereum-TEST",
"network": "Ethereum Sepolia",
"decimals": 6,
"exchange_rate": "1.0",
"deposit_address": "0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f",
"expired_at": 1748577793
}
},
"transactions": [
{
"tx_id": "4efe48fd-3b81-422a-8913-163d04d8c835",
"source_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"destination_address": "0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f",
"tx_hash": "0x85b9b9b2fcd8b149a79efa6eee0d94f0d81c6d3172d1335fed897a374e81210c",
"amount": "10000000",
"decimals": 6,
"asset_id": "USDC-Ethereum-TEST",
"token": "USDC",
"network": "Ethereum Sepolia",
"type": "deposit",
"created_at": 1748576344,
"status": "confirmed",
"aml_status": "approved",
"aml_info": "",
"charge_id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"refund_id": "",
"fee_info": "network_fee:\"0.00009323898359702\"",
"fee_currency": "ETH_TEST5"
}
],
"receipt_email": "",
"return_url": "",
"receipt_url": "",
"refunded": false,
"refunds": [],
"review": null,
"payer_max_payload": null,
"stripe_payload": null,
"payment_provider": ""
}
],
"receipt_email": "",
"return_url": "",
"status": "Success",
"payment_intent_status": "Confirmed",
"complete_on_first_payment": false,
"permanent_deposit": false,
"permanent_deposit_asset_id": "",
"expired_at": 0,
"unfreeze_withdraws": [],
"invoice": null,
"invoice_details": null,
"subscription": null,
"subscription_details": null
}
}

After successfully querying the payment intent and receiving a "Success" status, it indicates that the payment has been received. You can then examine the "transactions" array within the response to view detailed information about each transaction associated with the payment intent. This includes important details such as transaction IDs, blockchain addresses, transaction hashes, amounts, and confirmation statuses.

Additional Payment Intent Fields:

  • unfreeze_withdraws: Array of unfreeze withdrawal records for previously frozen funds. When a payment is initially rejected by AML (anti-money laundering) checks and later approved, this field contains the withdrawal records for releasing those frozen funds.
  • invoice: Invoice ID if this payment intent is associated with an invoice (used for subscription billing). This field is populated when the payment intent is created for a subscription invoice.
  • invoice_details: Expanded invoice details when using the expand=invoice query parameter. Contains full invoice information including line items, subscription details, and billing information.
  • subscription: Subscription ID resolved through the associated invoice. Only present when the payment intent is part of a subscription payment flow.
  • subscription_details: Expanded subscription details when using the expand=invoice.subscription query parameter. Includes subscription plan information, billing cycle, status, and related metadata.

Additional Charge Fields:

  • payer_max_payload: Contains PayerMax payment provider specific data when using PayerMax as the payment method. Includes fields like cashier_url (payment page URL), status, session_key, and client_key for handling PayerMax payments.
  • stripe_payload: Contains Stripe payment provider specific data when using Stripe as the payment method. Includes fields like client_secret (for Stripe Elements), public_key, status, and optional customer_id for saved payment methods.
  • payment_provider: Identifies the payment provider handling this charge. Can be values like "stripe", "payer_max", or empty for crypto payments. This helps determine which payload fields to use for payment processing.

Understanding the Response

Success Check: error_code: "success"

Key Status Fields:

  • status: Overall payment status (Processing, Success, Failed, Canceled)
  • payment_intent_status: Detailed status (Waiting, Confirmed, etc.)

Payment Confirmation:

  • Status "Success" = Payment received
  • Check transactions array for blockchain details
  • Use receipt_url for customer receipt

Next Steps: Fulfill order if status is Success, retry/refund if Failed, monitor if Processing.

5. List Payment Intents

Fetches a list of payment intents, with options for filtering and pagination. The customer parameter is optional and can be used to filter payment intents by a specific customer. You can also filter by merchant order ID, permanent deposit status and asset ID.

For detailed API reference, see: List Payment Intents API

Request:

curl --location --request GET 'https://api.martianpay.com/v1/payment_intents?page=0&page_size=10&customer=cus_7p51tXM7GBi7sz5J' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Query Parameters:

  • page (required): Page number, starting from 0
  • page_size (required): Number of items per page
  • customer (optional): Filter by customer ID
  • customer_email (optional): Filter by customer email
  • merchant_order_id (optional): Filter by merchant order ID
  • permanent_deposit (optional): Filter by permanent deposit status (true/false)
  • permanent_deposit_asset_id (optional): Filter by permanent deposit asset ID

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"payment_intents": [
{
"id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"payment_details": {
"amount_captured": {"asset_id": "USD", "amount": "10"},
"amount_refunded": {"asset_id": "USD", "amount": "0"},
"tx_fee": {"asset_id": "USD", "amount": "0.1"},
...
},
"canceled_at": 0,
"cancellation_reason": "",
"client_secret": "",
"created": 1748575946,
"updated": 1748576345,
"currency": "USD",
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"object": "customer",
"total_expense": 490,
"total_payment": 580,
"total_refund": 90,
...
},
"description": "Payment for Order #123",
"livemode": false,
"metadata": null,
"payment_link_details": null,
"merchant_order_id": "order_1234",
"payment_method_type": "crypto",
"charges": [
{
"id": "ch_c9tR5aQRdtmkKwCQDji9Z6ST",
"object": "charge",
"amount": {"asset_id": "USDC-Ethereum-TEST", "amount": "10"},
"payment_details": {...},
"exchange_rate": "1.0",
...
"payment_method_options": {
"crypto": {
"amount": "10000000",
"token": "USDC",
"asset_id": "USDC-Ethereum-TEST",
"network": "Ethereum Sepolia",
"decimals": 6,
"exchange_rate": "1.0",
"deposit_address": "0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f",
"expired_at": 1748577793
}
},
"transactions": [
{
"tx_id": "4efe48fd-3b81-422a-8913-163d04d8c835",
"source_address": "0x36279Ac046498bF0cb742622cCe22F3cE3c2AfD9",
"destination_address": "0x736e84F695504f9A65FD1B76c3E3cf01Eb43802f",
"tx_hash": "0x85b9b9b2fcd8b149a79efa6eee0d94f0d81c6d3172d1335fed897a374e81210c",
"amount": "10000000",
"decimals": 6,
"asset_id": "USDC-Ethereum-TEST",
"token": "USDC",
"network": "Ethereum Sepolia",
"type": "deposit",
"created_at": 1748576344,
"status": "confirmed",
...
}
],
...
}
],
"receipt_email": "",
"return_url": "",
"status": "Success",
"payment_intent_status": "Confirmed",
"complete_on_first_payment": false,
"permanent_deposit": false,
"permanent_deposit_asset_id": "",
"expired_at": 0,
"unfreeze_withdraws": [],
"invoice": null,
"invoice_details": null,
"subscription": null,
"subscription_details": null
},
...
],
"total": 8,
"page": 0,
"page_size": 10
}
}

Understanding the Response

Success Check: error_code: "success"

Key Fields:

  • payment_intents: Array of payment intent objects
  • total: Total count matching filter
  • page, page_size: Pagination info

Filtering: Use customer, merchant_order_id, permanent_deposit parameters to narrow results.

Next Steps: Loop through payment intents, check status, fulfill completed orders.

6. Cancel Payment Intent

Only payment intents with a status of "RequiresPaymentMethod" or those that have exceeded their payment timeout can be canceled. Attempts to cancel payment intents in any other status will result in a failure response.

For detailed API reference, see: Cancel Payment Intent API

Request:

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents/:id/cancel' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"reason": "Customer requested cancellation"
}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "pi_dqj7qywdZQ5Rk5R5nEJIJ7VF",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"payment_details": {
"amount_captured": {
"asset_id": "USD",
"amount": "0"
},
"amount_refunded": {
"asset_id": "USD",
"amount": "0"
},
"tx_fee": {
"asset_id": "USD",
"amount": "0"
},
"tax_fee": {
"asset_id": "USD",
"amount": "0"
},
"frozen_amount": {
"asset_id": "USD",
"amount": "0"
},
"net_amount": {
"asset_id": "USD",
"amount": "0"
},
"gas_fee": {},
"network_fee": {
"asset_id": "USD",
"amount": "0"
}
},
"canceled_at": 1748577604,
"cancellation_reason": "",
"client_secret": "pi_dqj7qywdZQ5Rk5R5nEJIJ7VF_secret_78V6DrBGY15GsPE4EljjEMZJB",
"created": 1748577592,
"updated": 1748577592,
"currency": "USD",
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"object": "customer",
"total_expense": 490,
"total_payment": 580,
"total_refund": 90,
"currency": "USD",
"created": 1737964162,
"name": "John Doe",
"email": "john@example.com",
"description": "VIP customer",
"metadata": {
"source": "web",
"user_id": "123"
},
"phone": "+1234567890"
},
"description": "Payment for Order #123",
"livemode": false,
"metadata": {
"customer_name": "John Doe",
"order_id": "order_123"
},
"payment_link_details": null,
"merchant_order_id": "order_1234",
"payment_method_type": "",
"charges": [],
"receipt_email": "",
"return_url": "",
"status": "Canceled",
"payment_intent_status": "Cancelled",
"complete_on_first_payment": false,
"permanent_deposit": false,
"permanent_deposit_asset_id": "",
"expired_at": 0
}
}

Error Response:

{
"code": 190,
"error_code": "status unexpected",
"msg": "status unexpected"
}

Understanding the Response

Success Check: error_code: "success" ✅ | status: "Canceled"

Cancellation Requirements:

  • ✅ Status must be "RequiresPaymentMethod"
  • ✅ OR payment timeout exceeded
  • ❌ Cannot cancel completed/processing payments

Error 190: Status unexpected = Cannot cancel this payment intent

Next Steps: Verify cancellation, issue refund separately if payment already completed.

7. Webhook Events for Payment Intents

MartianPay sends webhook notifications for payment intent events, enabling you to track the complete payment lifecycle in real-time.

7.1 Payment Intent Events

Event TypeDescriptionTriggered When
payment_intent.createdPayment intent createdA new payment intent is initially created
payment_intent.processingPayment processing startedPayment attempt begins (customer submitted payment)
payment_intent.succeededPayment completed successfullyFull payment amount received and confirmed
payment_intent.payment_failedPayment attempt failedPayment failed due to insufficient funds, network issues, or card declined
payment_intent.partially_paidPartial payment receivedOnly portion of amount received (crypto payments only)
payment_intent.canceledPayment intent canceledPayment intent canceled manually or expired

7.2 Webhook Payload Example

Here's an example of a payment intent webhook event payload:

{
"id": "evt_056qw9fr9PndljykFUqSIf6t",
"object": "event",
"api_version": "2025-01-22",
"created": 1748576345,
"data": {
"object": {
"id": "pi_vfk7mLTHPs7Cf0UbU38tGiKq",
"object": "payment_intent",
"amount": {
"asset_id": "USD",
"amount": "10"
},
"currency": "USD",
"status": "Success",
"payment_intent_status": "Confirmed",
"created": 1748575946,
"updated": 1748576345,
"customer": {
"id": "cus_7p51tXM7GBi7sz5J",
"name": "John Doe",
"email": "john@example.com"
},
"merchant_order_id": "order_1234",
"payment_method_type": "crypto",
"description": "Payment for Order #123",
"metadata": {
"order_id": "order_123",
"customer_name": "John Doe"
}
},
"previous_attributes": {
"status": "Processing",
"payment_intent_status": "Waiting",
"updated": 1748576000
}
},
"livemode": false,
"pending_webhooks": 0,
"type": "payment_intent.succeeded"
}

7.3 Webhook Event Flow

Typical Payment Intent Event Sequence:

For Crypto Payments:

  1. payment_intent.created - Payment intent created
  2. payment_intent.processing - Customer sends crypto payment
  3. payment_intent.succeeded - Payment confirmed on blockchain
    • OR payment_intent.partially_paid - Partial amount received
    • OR payment_intent.payment_failed - Payment failed or expired

For Card Payments:

  1. payment_intent.created - Payment intent created
  2. payment_intent.processing - Card payment processing
  3. payment_intent.succeeded - Card charged successfully
    • OR payment_intent.payment_failed - Card declined or processing error

Status Transitions:

created → processing → succeeded (SUCCESS)
↘ payment_failed (FAILURE)
↘ partially_paid (CRYPTO ONLY, PARTIAL)
created → canceled (MANUAL CANCELLATION or EXPIRATION)

7.4 Best Practices

  1. Monitor payment_intent.succeeded events - This is your confirmation that payment is complete:

    • Fulfill orders immediately
    • Update order status to "paid"
    • Send confirmation emails to customers
    • Update inventory if applicable
  2. Alert on payment_intent.payment_failed events - Take action when payments fail:

    • Notify customers of failed payment
    • Provide alternative payment options
    • Log failure reasons for analysis
    • Send retry payment links if appropriate
  3. Handle partial payments (crypto) - Monitor payment_intent.partially_paid events:

    • Notify customers they sent insufficient amount
    • Provide instructions to complete payment
    • Consider refunding partial amounts or accepting as partial fulfillment
  4. Track processing status - Use payment_intent.processing to:

    • Show real-time status to customers ("Processing your payment...")
    • Track time from submission to completion
    • Identify slow processing for investigation
  5. Handle idempotency - Store the event ID (evt_*) to prevent duplicate order fulfillment

  6. Reconcile with merchant_order_id - Use the merchant_order_id field to:

    • Match webhook events to your internal orders
    • Update order status in your system
    • Prevent duplicate processing
  7. Monitor canceled payments - Track payment_intent.canceled events:

    • Clean up abandoned checkouts
    • Release reserved inventory
    • Analyze cancellation reasons
  8. Extract transaction details - When payment_intent.succeeded is received:

    • Store transaction hashes from charges[].transactions[]
    • Provide blockchain explorer links to customers
    • Keep records for accounting and reconciliation

For more details on webhook setup, signature verification, and complete event schemas, see the Webhook Events documentation.