Launch checklist and traps

What must be true before you create a real subscription, followed by the traps that account for nearly every support question we have seen. Each one links to the page that explains it.

Before launch

The sandbox clock tools are not under /api

https://sandbox.invo.network/sandbox/subscriptions/<id>/advance-clock, not .../sandbox/api/subscriptions/.... The wrong path is a plain 404 that looks like a missing feature. Every other subscription call is under /api.

Card traps

  1. Replay before retry. On any timeout or 5xx from /subscribe, send the same body again. A new client_request_id in a retry loop lands on 409 ACTIVE_SUBSCRIPTION_EXISTS at best and a second subscription at worst.
  2. requires_action is not paid. Nothing has been charged. Do not grant paid access until subscription.renewed.
  3. pending is not failed. Wait for the event; Invo resolves it.
  4. current_period_end is not entitlement. Use paid_through.
  5. payment_failed and past_due do not revoke access. Only expired (at final_period_end) and canceled (at access_until) do.
  6. authentication_required is not a failure. Do not send the member a “payment failed” message; send them the link.
  7. Do not poll for renewals. Subscribe to subscription.renewed.
  8. One live subscription per item, on either rail. Cancel the old one before creating a new one for the same item_id, or change the price with /amount.
  9. At-period-end cancel on an unpaid subscription is immediate. Read terminated_immediately on the response.
  10. Price changes apply next period, and big jumps do not apply at all. More than double, or above 500.00, stays pending forever.
  11. A card saved without off-session consent cannot back a subscription. Cards saved through saved_card_id purchases, through hosted checkout, or before the mandate change need to be saved again through /setup-intent or a save_card: true purchase.
  12. wallet_only is absolute. A card attached later is ignored until you pass wallet_only: false on /payment-method.
  13. sub_ is reserved as a prefix for every idempotency key on the platform, not only here.
  14. Metadata is yours except _invo. Invo writes cancellation reasons under that key.
  15. Send consent. It is what a disputed charge is argued from.
  16. Do not store or log confirmation_url beside anything you publish. It is a bearer link.
  17. subscription.period_seq is the next period, not the paid one. After a paid first charge it already reads 2. Key entitlement on paid_period_seq and on the period_seq of subscription.renewed.

Steam traps

  1. Finalize is your call. Steam does not tell Invo the member authorised; your game must call /steam/finalize after the overlay or checkout closes, and again if it returned not_authorized.
  2. Match the client callback on steam_order_id, never on steam_transid. Store it against the subscription_id before you return from init.
  3. 24 hours to authorise. An unfinalised subscription expires and must be re-created with a new key.
  4. web sessions need player_ip and the checkout URL must open top-level; it cannot be framed.
  5. Only the primary key or the steam channel key may start a Steam subscription.
  6. No trials, no interval_count other than 1, no wallet_only, no card fields.
  7. The price is a ceiling. Increases are refused; start a new Steam subscription. Decreases stage.
  8. /payment-method is refused on a Steam subscription.
  9. Coins per period depend on the member’s country and are fixed at init. Show amount_coins from the init response, not price times 10.
  10. An empty wallet pays the full price; a partial wallet pays only for the missing coins. Both are correct and both appear as funding.steam_charged_usd.
  11. A member can cancel from Steam. You learn it from subscription.canceled with canceled_by: "steam"; access runs to access_until.
  12. Some Steam renewals are deferred for reasons on your side (billing setup, credit, Steam configuration) with no event. Keep your Invo billing setup healthy and watch next_charge_at.
  13. Replaying /steam/init returns the row’s current status, which may already be active, canceled or expired.
  14. A second init while one is pending reuses it. Same terms, same Steam account and same usersession: you get the pending row back with pending_reuse: true and the same steam_order_id, and no second agreement is opened. Anything else: 409 STEAM_AUTHORIZATION_PENDING carrying the pending subscription_id.
  15. Reuse is deliberately narrow, and narrower is not the same as safer. It would be easy to read the rule as “reuse whenever the price matches, because a duplicate agreement is the danger”. It is not. An authorisation belongs to one Steam account and one session type: hand a member an agreement her current Steam account cannot see, or a web checkout link when she is in the overlay, and she is stuck for a full day with no way to act. A rival agreement she can answer is the lesser harm, so the rule fails toward “she can act” rather than toward “nothing is duplicated”. When you get the 409, show her the pending one and let her finalize or cancel it.
  16. Same item_id as the card version of the membership. 409 ACTIVE_SUBSCRIPTION_EXISTS across rails is the guard against double-billing, not a bug.
  17. Refunds of Steam-charged periods are not supported yet. /refund answers 409 STEAM_REFUND_NOT_SUPPORTED; wallet-covered periods refund in coins.
  18. Sandbox does not exercise Valve. sandbox_auto_approved: true means the charge was simulated; do one real Steam sandbox authorisation before launch.
  19. split.total_usd on Steam is not the price. It is what the coins are worth after Valve’s share and VAT; the price is the top-level amount_usd.
  20. Two expiries arrive with no payment_failed before them: steam_authorization_abandoned and steam_containment. Handle subscription.expired on its own.

Every code behind these traps is on the error reference.