Skip to main content
POST
/
v2
/
payment-links

Create Payment Link

Generate a payment link that customers can use to complete checkout. Payment links automatically handle HSA/FSA eligibility detection and can be used for both one-time and recurring payments.

Authentication

Authorization: Bearer glm_test_YOUR_API_KEY

Request Body

{
  "amount_cents": 4995,
  "currency": "USD",
  "description": "Premium Blood Pressure Monitor",
  "customer": {
    "email": "customer@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  },
  "products": [
    {
      "merchant_product_id": "BP-MONITOR-001",
      "name": "Blood Pressure Monitor",
      "quantity": 1,
      "price_cents": 4995
    }
  ],
  "payment_type": "one_time",
  "success_url": "https://yoursite.com/success",
  "cancel_url": "https://yoursite.com/cancel",
  "metadata": {
    "order_id": "ORD-12345",
    "customer_id": "CUST-789"
  }
}

Parameters

ParameterTypeRequiredDescription
amount_centsintegerYesTotal amount in cents
currencystringYesISO 4217 currency code (e.g., “USD”)
descriptionstringYesDescription of the payment
customerobjectNoPre-fill customer information
customer.emailstringNoCustomer email
customer.first_namestringNoCustomer first name
customer.last_namestringNoCustomer last name
productsarrayNoProduct line items
products[].merchant_product_idstringYes*Your product reference ID
products[].namestringYes*Product name
products[].quantityintegerYes*Quantity
products[].price_centsintegerYes*Unit price in cents
payment_typeenumNoone_time or subscription (default: one_time)
subscriptionobjectNoRequired if payment_type is subscription
subscription.intervalenumYes*daily, weekly, monthly, yearly
subscription.interval_countintegerNoNumber of intervals (default: 1)
subscription.trial_period_daysintegerNoFree trial days
success_urlstringYesRedirect URL after successful payment
cancel_urlstringNoRedirect URL if customer cancels
metadataobjectNoCustom key-value pairs
expires_attimestampNoWhen link expires (default: 15 days)

Response

{
  "id": "plink_abc123xyz",
  "url": "https://checkout.withgale.com/pay/plink_abc123xyz",
  "amount_cents": 4995,
  "currency": "USD",
  "description": "Premium Blood Pressure Monitor",
  "payment_type": "one_time",
  "status": "active",
  "customer": {
    "email": "customer@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  },
  "products": [
    {
      "merchant_product_id": "BP-MONITOR-001",
      "name": "Blood Pressure Monitor",
      "quantity": 1,
      "price_cents": 4995,
      "is_hsa_fsa_eligible": true,
      "eligibility_type": "auto_substantiation"
    }
  ],
  "success_url": "https://yoursite.com/success",
  "cancel_url": "https://yoursite.com/cancel",
  "metadata": {
    "order_id": "ORD-12345",
    "customer_id": "CUST-789"
  },
  "expires_at": "2025-11-02T14:30:00Z",
  "created_at": "2025-10-18T14:30:00Z"
}

Examples

One-Time Payment

curl -X POST https://api.withgale.com/v2/payment-links \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 2995,
    "currency": "USD",
    "description": "Digital Thermometer",
    "products": [
      {
        "merchant_product_id": "THERM-001",
        "name": "Digital Thermometer",
        "quantity": 1,
        "price_cents": 2995
      }
    ],
    "success_url": "https://yoursite.com/success"
  }'

Subscription Payment

curl -X POST https://api.withgale.com/v2/payment-links \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 9900,
    "currency": "USD",
    "description": "Monthly Wellness Membership",
    "payment_type": "subscription",
    "subscription": {
      "interval": "monthly",
      "interval_count": 1,
      "trial_period_days": 7
    },
    "customer": {
      "email": "member@example.com"
    },
    "success_url": "https://yoursite.com/welcome",
    "metadata": {
      "membership_tier": "premium"
    }
  }'

Pre-filled Customer Information

const createPaymentLink = async (customerId, tier) => {
  const response = await fetch('https://api.withgale.com/v2/payment-links', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.GALE_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount_cents: tier === 'premium' ? 14900 : 9900,
      currency: 'USD',
      description: `${tier} Membership`,
      payment_type: 'subscription',
      subscription: {
        interval: 'monthly'
      },
      customer: {
        email: 'customer@example.com',
        first_name: 'John',
        last_name: 'Smith'
      },
      success_url: `https://yoursite.com/welcome?tier=${tier}`,
      cancel_url: 'https://yoursite.com/pricing',
      metadata: {
        customer_id: customerId,
        tier: tier,
        source: 'website'
      }
    })
  });

  const link = await response.json();
  return link.url;
};

Multiple Products

{
  "amount_cents": 7990,
  "currency": "USD",
  "description": "Wellness Starter Kit",
  "products": [
    {
      "merchant_product_id": "THERM-001",
      "name": "Digital Thermometer",
      "quantity": 1,
      "price_cents": 2995
    },
    {
      "merchant_product_id": "BP-MONITOR-001",
      "name": "Blood Pressure Monitor",
      "quantity": 1,
      "price_cents": 4995
    }
  ],
  "success_url": "https://yoursite.com/success"
}

Subscription Intervals

Intervalinterval_countBilling Frequency
daily1Every day
daily7Every 7 days
weekly1Every week
weekly2Every 2 weeks (bi-weekly)
monthly1Every month
monthly3Every 3 months (quarterly)
yearly1Every year
StatusDescription
activeLink is active and ready for payment
expiredLink has expired (past expires_at time)
paidPayment completed successfully
cancelledLink was cancelled

Webhooks

Payment links trigger the following webhook events:
  • payment_link.created - Link created
  • payment_link.paid - Payment completed
  • payment_link.expired - Link expired
  • order.created - Order created from payment
See Webhooks Reference for details.

Errors

Status CodeError CodeDescription
400invalid_requestMissing or invalid parameters
400invalid_amountAmount must be positive integer in cents
400invalid_currencyUnsupported currency code
401unauthorizedInvalid or missing API key
422validation_errorField validation failed
429rate_limit_exceededToo many requests
Example error:
{
  "error": {
    "code": "validation_error",
    "message": "Invalid subscription configuration",
    "details": [
      {
        "field": "subscription.interval",
        "message": "Interval is required for subscription payments"
      }
    ]
  }
}

Use Cases

Email Invoices

// Send payment link via email
const sendInvoice = async (customerId, amount) => {
  const link = await createPaymentLink({
    amount_cents: amount,
    currency: 'USD',
    description: 'Monthly Invoice',
    customer: await getCustomer(customerId),
    success_url: 'https://yoursite.com/invoice-paid'
  });

  await sendEmail({
    to: customer.email,
    subject: 'Your Invoice is Ready',
    body: `Click here to pay: ${link.url}`
  });
};

Membership Checkout

// Generate link for membership tier selection
const membershipTiers = {
  basic: { amount: 9900, name: 'Basic Membership' },
  premium: { amount: 14900, name: 'Premium Membership' },
  elite: { amount: 29900, name: 'Elite Membership' }
};

app.post('/select-tier', async (req, res) => {
  const tier = membershipTiers[req.body.tier];

  const link = await createPaymentLink({
    amount_cents: tier.amount,
    description: tier.name,
    payment_type: 'subscription',
    subscription: { interval: 'monthly' },
    success_url: `https://yoursite.com/welcome?tier=${req.body.tier}`
  });

  res.json({ checkout_url: link.url });
});

QR Code Checkout

// Generate QR code for in-store payment
const QRCode = require('qrcode');

const generateQRPayment = async (productId, amount) => {
  const link = await createPaymentLink({
    amount_cents: amount,
    currency: 'USD',
    description: 'In-Store Purchase',
    products: [{ merchant_product_id: productId, /* ... */ }],
    success_url: 'https://yoursite.com/receipt',
    metadata: { location: 'Store #1' }
  });

  const qrCode = await QRCode.toDataURL(link.url);
  return qrCode; // Display in store
};

Best Practices

Set Expiration

Use expires_at to prevent stale links from being used

Pre-fill Customer Data

Include customer info to speed up checkout

Use Metadata

Track links with custom metadata for analytics

Handle Webhooks

Listen for payment_link.paid to fulfill orders