Skip to content

feat: correct subscription response and statuses - #10566

Merged
tuna1207 merged 3 commits into
mainfrom
fix/subscription-response-type
Sep 29, 2026
Merged

tuna1207 merged 3 commits into
mainfrom
fix/subscription-response-type

Conversation

@tuna1207

@tuna1207 tuna1207 commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Explanation

The crypto subscription start flow and getSubscriptions were both failing on successful API responses because our response structs did not match what the Subscription API actually returns.

Crypto start response shape

SubscriptionService:startSubscriptionWithCrypto validated the POST /subscriptions/crypto response against StartCryptoSubscriptionResponseStruct ({ subscriptionId, status }). The API actually returns the full created Subscription object (crypto subscriptions are created immediately, unlike card checkout which returns a checkout session URL). Because the response never had a subscriptionId field, create() threw on every successful call, so the crypto start flow always failed after the API had already created the subscription.

This PR:

  • Removes StartCryptoSubscriptionResponseStruct and validates the response against SubscriptionStruct instead.
  • Redefines StartCryptoSubscriptionResponse as an alias of Subscription. This is BREAKING for consumers reading response.subscriptionId; they should read response.id instead. response.status is unchanged. This affects SubscriptionService:startSubscriptionWithCrypto, SubscriptionController:startSubscriptionWithCrypto, and SubscriptionDelegationService:startSubscriptionWithDelegation.

Optional fields on Subscription

SubscriptionStruct required lastInvoice.updatedAt and paymentMethod.card.displayBrand, but both are optional in the API. Any subscription with a lastInvoice (i.e. anything that has been billed at least once) failed validation and getSubscriptions threw. Both fields are now optional in the struct and on the SubscriptionInvoice / SubscriptionCardPaymentMethod types.

awaiting_funds status

The API returns an awaiting_funds status for crypto subscriptions that were created but whose first invoice has not been funded yet. This status was not in SUBSCRIPTION_STATUSES, so such subscriptions also failed validation. This PR adds SUBSCRIPTION_STATUSES.awaitingFunds and teaches SubscriptionController:submitSubscriptionCryptoApproval to treat it like past_due / unpaid: submitting a new approval for a subscription in this state updates the existing subscription's payment method instead of attempting to start a new one.

Tests

  • SubscriptionService.test.ts: crypto start tests now use full subscription fixtures; added cases for a missing updatedAt, a missing displayBrand, the awaiting_funds status, pass-through of additional subscription fields, and rejection of a non-subscription response.
  • SubscriptionController.test.ts and SubscriptionDelegationService.test.ts: updated to the new response shape and added coverage for the awaiting_funds branch in submitSubscriptionCryptoApproval.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Breaking return type on crypto subscription start affects all consumers; changes subscription status handling and crypto approval routing for payment recovery.

Overview
Fixes crypto subscription and getSubscriptions flows that threw on successful API responses because client validation did not match the Subscription API.

Breaking: StartCryptoSubscriptionResponse is now the full created Subscription (validated with SubscriptionStruct), not { subscriptionId, status }. Callers must use response.id instead of response.subscriptionId on startSubscriptionWithCrypto and delegation start paths.

Validation fixes: lastInvoice.updatedAt and card displayBrand are optional so billed subscriptions and card payment methods no longer fail getSubscriptions. Adds SUBSCRIPTION_STATUSES.awaitingFunds for crypto subs waiting on first-invoice funding.

Behavior: submitSubscriptionCryptoApproval treats awaiting_funds like past_due / unpaid—a new approval updates the existing subscription’s payment method instead of starting a new subscription.

Tests and changelog updated for the new response shape, optional fields, and the awaiting-funds approval branch.

Reviewed by Cursor Bugbot for commit 82a2b98. Bugbot is set up for automated code reviews on this repo. Configure here.

@tuna1207
tuna1207 marked this pull request as ready for review September 29, 2026 11:50
@tuna1207
tuna1207 requested review from a team as code owners September 29, 2026 11:50
@tuna1207
tuna1207 deployed to default-branch September 29, 2026 11:50 — with GitHub Actions Active
@tuna1207

Copy link
Copy Markdown
Member Author

@metamaskbot publish-previews

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@11.0.0-preview-82a2b9842
@metamask-previews/accounts-controller@40.0.0-preview-82a2b9842
@metamask-previews/address-book-controller@8.0.0-preview-82a2b9842
@metamask-previews/advanced-chart-core@1.0.0-preview-82a2b9842
@metamask-previews/ai-controllers@2.0.0-preview-82a2b9842
@metamask-previews/analytics-controller@3.2.0-preview-82a2b9842
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-82a2b9842
@metamask-previews/announcement-controller@9.0.0-preview-82a2b9842
@metamask-previews/app-metadata-controller@3.0.0-preview-82a2b9842
@metamask-previews/approval-controller@10.0.0-preview-82a2b9842
@metamask-previews/assets-controller@17.0.0-preview-82a2b9842
@metamask-previews/assets-controllers@112.0.4-preview-82a2b9842
@metamask-previews/authenticated-user-storage@4.1.0-preview-82a2b9842
@metamask-previews/base-controller@10.0.0-preview-82a2b9842
@metamask-previews/base-data-service@2.1.0-preview-82a2b9842
@metamask-previews/bitcoin-regtest-up@2.0.0-preview-82a2b9842
@metamask-previews/bridge-controller@81.3.3-preview-82a2b9842
@metamask-previews/bridge-status-controller@76.3.3-preview-82a2b9842
@metamask-previews/build-utils@4.0.0-preview-82a2b9842
@metamask-previews/chain-agnostic-permission@2.0.0-preview-82a2b9842
@metamask-previews/chomp-api-service@6.0.0-preview-82a2b9842
@metamask-previews/claims-controller@1.0.2-preview-82a2b9842
@metamask-previews/client-controller@2.0.0-preview-82a2b9842
@metamask-previews/client-utils@3.0.3-preview-82a2b9842
@metamask-previews/compliance-controller@3.0.0-preview-82a2b9842
@metamask-previews/composable-controller@13.0.0-preview-82a2b9842
@metamask-previews/config-registry-controller@4.0.0-preview-82a2b9842
@metamask-previews/connectivity-controller@1.0.0-preview-82a2b9842
@metamask-previews/controller-utils@13.0.0-preview-82a2b9842
@metamask-previews/core-backend@11.0.0-preview-82a2b9842
@metamask-previews/cryptography@0.0.0-preview-82a2b9842
@metamask-previews/delegation-controller@4.0.0-preview-82a2b9842
@metamask-previews/earn-controller@13.0.2-preview-82a2b9842
@metamask-previews/eip-5792-middleware@4.0.1-preview-82a2b9842
@metamask-previews/eip-7702-internal-rpc-middleware@1.0.0-preview-82a2b9842
@metamask-previews/eip1193-permission-middleware@3.0.0-preview-82a2b9842
@metamask-previews/eth-block-tracker@16.0.0-preview-82a2b9842
@metamask-previews/eth-json-rpc-middleware@25.0.0-preview-82a2b9842
@metamask-previews/eth-json-rpc-provider@7.0.0-preview-82a2b9842
@metamask-previews/foundryup@2.0.0-preview-82a2b9842
@metamask-previews/gas-fee-controller@27.0.0-preview-82a2b9842
@metamask-previews/gator-permissions-controller@6.0.1-preview-82a2b9842
@metamask-previews/geolocation-controller@2.0.0-preview-82a2b9842
@metamask-previews/java-tron-up@2.0.0-preview-82a2b9842
@metamask-previews/json-rpc-engine@11.0.0-preview-82a2b9842
@metamask-previews/json-rpc-middleware-stream@9.0.0-preview-82a2b9842
@metamask-previews/keyring-controller@28.1.0-preview-82a2b9842
@metamask-previews/kyc-controller@0.6.0-preview-82a2b9842
@metamask-previews/local-node-utils@2.0.0-preview-82a2b9842
@metamask-previews/logging-controller@10.0.0-preview-82a2b9842
@metamask-previews/message-manager@15.0.0-preview-82a2b9842
@metamask-previews/messenger@3.0.0-preview-82a2b9842
@metamask-previews/messenger-cli@1.0.0-preview-82a2b9842
@metamask-previews/money-account-api-data-service@2.1.0-preview-82a2b9842
@metamask-previews/money-account-balance-service@3.1.1-preview-82a2b9842
@metamask-previews/money-account-controller@2.0.0-preview-82a2b9842
@metamask-previews/money-account-upgrade-controller@5.1.0-preview-82a2b9842
@metamask-previews/money-account-utils@2.1.0-preview-82a2b9842
@metamask-previews/multichain-account-service@14.1.0-preview-82a2b9842
@metamask-previews/multichain-api-middleware@5.0.0-preview-82a2b9842
@metamask-previews/multichain-network-controller@4.0.0-preview-82a2b9842
@metamask-previews/multichain-transactions-controller@8.0.0-preview-82a2b9842
@metamask-previews/name-controller@10.0.0-preview-82a2b9842
@metamask-previews/network-connection-banner-controller@1.0.0-preview-82a2b9842
@metamask-previews/network-controller@37.0.0-preview-82a2b9842
@metamask-previews/network-enablement-controller@7.0.1-preview-82a2b9842
@metamask-previews/notification-services-controller@29.0.2-preview-82a2b9842
@metamask-previews/passkey-controller@4.1.0-preview-82a2b9842
@metamask-previews/permission-controller@14.0.0-preview-82a2b9842
@metamask-previews/permission-log-controller@6.0.0-preview-82a2b9842
@metamask-previews/perps-controller@18.0.1-preview-82a2b9842
@metamask-previews/phishing-controller@18.1.1-preview-82a2b9842
@metamask-previews/platform-api-docs@0.2.1-preview-82a2b9842
@metamask-previews/polling-controller@17.0.0-preview-82a2b9842
@metamask-previews/preferences-controller@24.0.0-preview-82a2b9842
@metamask-previews/profile-metrics-controller@5.1.2-preview-82a2b9842
@metamask-previews/profile-sync-controller@33.0.0-preview-82a2b9842
@metamask-previews/ramps-controller@26.0.1-preview-82a2b9842
@metamask-previews/rate-limit-controller@8.0.0-preview-82a2b9842
@metamask-previews/react-data-query@2.0.0-preview-82a2b9842
@metamask-previews/remote-feature-flag-controller@7.0.0-preview-82a2b9842
@metamask-previews/sample-controllers@6.0.0-preview-82a2b9842
@metamask-previews/seedless-onboarding-controller@11.0.1-preview-82a2b9842
@metamask-previews/selected-network-controller@27.0.0-preview-82a2b9842
@metamask-previews/sentinel-api-service@2.0.0-preview-82a2b9842
@metamask-previews/shield-controller@7.0.3-preview-82a2b9842
@metamask-previews/signature-controller@40.0.0-preview-82a2b9842
@metamask-previews/smart-transactions-controller@27.0.3-preview-82a2b9842
@metamask-previews/snap-account-service@4.0.0-preview-82a2b9842
@metamask-previews/social-controllers@3.4.0-preview-82a2b9842
@metamask-previews/solana-test-validator-up@2.0.0-preview-82a2b9842
@metamask-previews/stellar-quickstart-up@0.0.0-preview-82a2b9842
@metamask-previews/storage-service@2.0.0-preview-82a2b9842
@metamask-previews/subscription-controller@10.0.1-preview-82a2b9842
@metamask-previews/transaction-controller@72.0.1-preview-82a2b9842
@metamask-previews/transaction-pay-controller@29.2.3-preview-82a2b9842
@metamask-previews/user-operation-controller@42.0.1-preview-82a2b9842
@metamask-previews/utils@12.0.0-preview-82a2b9842
@metamask-previews/wallet@15.0.1-preview-82a2b9842
@metamask-previews/wallet-cli@0.0.0-preview-82a2b9842

@tuna1207
tuna1207 added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit f6f4999 Sep 29, 2026
54 checks passed
@tuna1207
tuna1207 deleted the fix/subscription-response-type branch September 29, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants