Membership Payments
Recover failed membership payments, share payment-method links, and choose billing addresses
These endpoints let an authenticated customer manage payments for their own memberships. Every request requires a JWT bearer token and a linked customer profile. Obtain the membershipId from List Memberships.
Responses use top-level recovery, setup, link, or billingAddress fields rather than a data envelope. Amounts are in minor currency units; timestamps are Unix milliseconds.
Inspect a payment
GET /membership-payments/recovery?membershipId={membershipId}curl "https://edge.tiquo.app/api/client/v1/membership-payments/recovery?membershipId=membership_123" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."{
"success": true,
"recovery": {
"membershipId": "membership_123",
"planName": "Gold Member",
"state": "requires_payment_method",
"recoverable": true,
"invoiceId": "in_123",
"invoiceStatus": "open",
"paymentIntentStatus": "requires_payment_method",
"clientSecret": null,
"amountDue": 2500,
"currency": "gbp",
"invoiceCreatedAt": 1790812800000,
"nextPaymentAttemptAt": null,
"failureReason": "expired_card",
"paymentMethod": {
"brand": "visa",
"last4": "4242",
"expMonth": 9,
"expYear": 2026
},
"canRetrySavedPaymentMethod": true,
"reconciliationPending": false
}
}| Field | Meaning |
|---|---|
membershipId, planName | The owned membership and its plan name |
state | Current state from the table below |
recoverable | Whether an eligible outstanding invoice can be recovered for this active or suspended membership |
invoiceId, invoiceStatus, paymentIntentStatus | Payment-provider details; nullable when unavailable |
clientSecret | Stripe confirmation secret when state is requires_action; otherwise null |
amountDue, currency | Outstanding amount in minor units and invoice currency; 0 and an empty currency when no invoice is available |
invoiceCreatedAt, nextPaymentAttemptAt | Unix timestamps in milliseconds, or null |
failureReason | authentication_required, expired_card, insufficient_funds, or payment_failed |
paymentMethod | Saved-card summary (brand, last4, expMonth, expYear), or null |
canRetrySavedPaymentMethod | Whether a saved card is available; also check recoverable before offering a retry |
reconciliationPending | Whether a paid Stripe invoice still needs reconciliation with Tiquo |
| State | Client behaviour |
|---|---|
requires_payment_method | Offer a saved-card retry when available, or collect a new card |
requires_action | Use the returned clientSecret with Stripe's client SDK to complete authentication, then call finalize |
processing | Wait and refresh the recovery state; do not show the invoice as paid yet |
paid | Payment is paid; check reconciliationPending before treating the Tiquo update as complete |
unavailable | No eligible invoice is available for recovery |
An HTTP 200 means the request succeeded. It does not mean the invoice has been paid: inspect recovery.state.
Recover a payment
POST /membership-payments/recovery| Field | Required | Description |
|---|---|---|
membershipId | Yes | The customer's membership ID |
operation | Yes | One of the operations below |
setupIntentId | For confirm_payment_method | Succeeded SetupIntent created by this membership recovery flow |
| Operation | Behaviour | Response field |
|---|---|---|
retry | Attempt payment with the saved card; may return a required authentication step | recovery |
create_payment_method_setup | Create a card-only SetupIntent for this membership and invoice | setup |
confirm_payment_method | Save the confirmed card as the Stripe customer and membership default, then attempt payment | recovery |
finalize | Refresh payment state and reconcile a paid invoice with Tiquo | recovery |
Retry a saved card
curl -X POST "https://edge.tiquo.app/api/client/v1/membership-payments/recovery" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{"membershipId":"membership_123","operation":"retry"}'The response has the same recovery object as the inspection endpoint. Follow the returned state, including any authentication step.
Replace the card
- Send
{"membershipId":"membership_123","operation":"create_payment_method_setup"}. - Collect and confirm the new card with Stripe's client SDK using
setup.clientSecret. - After the SetupIntent succeeds, send
{"membershipId":"membership_123","operation":"confirm_payment_method","setupIntentId":"seti_123"}. - Inspect
recovery.state. If it isrequires_action, complete the Stripe authentication step and send{"membershipId":"membership_123","operation":"finalize"}.
The setup response is:
{
"success": true,
"setup": {
"setupIntentId": "seti_123",
"clientSecret": "seti_123_secret_..."
}
}This flow accepts cards only. A SetupIntent from the general /payment-methods/setup endpoint, another customer, another membership, or another invoice is rejected. Only a succeeded recovery SetupIntent can be confirmed.
Use finalize after client-side payment confirmation. If the returned state is paid with reconciliationPending: true, retry finalisation later to reconcile Tiquo's membership and order records.
Create a payment-method link
POST /membership-payments/payment-method-linkCreate a link the customer can share with another person, such as their company, to supply the membership's payment method. This request creates the link; the customer shares it themselves. It does not send an email.
curl -X POST "https://edge.tiquo.app/api/client/v1/membership-payments/payment-method-link" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{"membershipId":"membership_123"}'The response contains success: true and link: { url, expiresAt }. Use the returned URL unchanged; expiresAt is its expiry time in Unix milliseconds. The membership must belong to the customer and be eligible for a Stripe subscription payment-method update.
Choose a billing address
POST /membership-payments/billing-addressChoose the address used on invoices from the next renewal. Cancelled or expired memberships cannot be updated.
| Field | Required | Description |
|---|---|---|
membershipId | Yes | The customer's membership ID |
billingAddressId | No | An existing address belonging to the customer, with type billing or both |
address | No | A new billing address to save on the customer profile |
For address, supply non-empty addressLine1, city, postalCode, and country. Optional fields are company, addressLine2, and state. The company is included in the billing address used for the invoice.
curl -X POST "https://edge.tiquo.app/api/client/v1/membership-payments/billing-address" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"membershipId": "membership_123",
"address": {
"company": "Acme Ltd",
"addressLine1": "10 Example Street",
"city": "London",
"postalCode": "SW1A 1AA",
"country": "GB"
}
}'If both address and billingAddressId are supplied, address takes precedence. An identical saved address is reused where available. A newly saved address becomes the customer's default only when they have no billing address yet.
To restore the default, send only {"membershipId":"membership_123"}. The membership then uses the customer's default billing address, or the first available billing address when none is marked as default.
{
"success": true,
"billingAddress": {
"_id": "address_123",
"chosen": true,
"company": "Acme Ltd",
"addressLine1": "10 Example Street",
"city": "London",
"postalCode": "SW1A 1AA",
"country": "GB"
}
}chosen is true when this membership explicitly selects the address and false when it falls back to a customer billing address. Other optional response fields are label, addressLine2, and state. billingAddress is null when no billing address is available. Existing invoices are not rewritten.
Errors
| Status | Description |
|---|---|
400 | Missing or invalid request data, membership unavailable or not owned, an ineligible payment or address, or a payment-provider operation that could not be completed |
401 | Missing, invalid, or expired access token |
403 | No customer profile is linked to the account |
409 | The membership is held in a connected system such as HubSpot (ID starts with ext_, code external_membership) and is managed there |
Errors return {"success":false,"error":"Description of what went wrong"}. These membership-payment handlers return 400 for operational failures, including unavailable memberships; do not assume those failures return 404 or 500.