Skip to content

feat(payment_terms): Wallet payment term - #6580

Draft
annvelents wants to merge 1 commit into
feat-payment-terms-one-offfrom
feat-payment-terms-wallets
Draft

annvelents wants to merge 1 commit into
feat-payment-terms-one-offfrom
feat-payment-terms-wallets

Conversation

@annvelents

Copy link
Copy Markdown
Contributor

Context

Wallet top-ups produce credit invoices. A wallet can now carry its own payment term, overriding the customer and billing entity terms on every top-up invoice.

Linear: BIL-522. Unblocks the frontend work in BIL-675. The wallets.payment_term column was added in #6231.

Description

Resolution. Every top-up path (manual, interval, threshold, API) goes through Invoices::PaidCreditService.

  • Wallet has a term: the credit invoice snapshots it with payment_term_source: "wallet".
  • No wallet term: resolution is unchanged: customer → issuing billing entity → due_on_receipt.

The term lives on the wallet only. Recurring rules and individual transactions have no term of their own.

Payment-gated top-ups (invoice_requires_successful_payment) keep today's behaviour, per the product decision. FinalizeOpenCreditService moves both the issuing date and the due date to the payment day, and leaves the term snapshot untouched. A spec pins this.

Writes and reads.

  • Wallets::CreateService and UpdateService validate and store the normalized payment_term. On update, null clears it.
  • POST /api/v1/wallets, PUT /api/v1/wallets/:id and the customer-scoped wallet endpoints accept payment_term, through RawPaymentTermParams.
  • The wallet serializer returns payment_term: the wallet's own term, or null when it inherits. The wallet.* webhooks gain the field.
  • The createCustomerWallet and updateCustomerWallet mutations accept paymentTerm. Wallet.paymentTerm resolves through PaymentTerm.from_h.

Validation

Specs cover:

  • credit invoices with and without a wallet term;
  • the payment-gated finalization pin;
  • service create, update, clear and invalid input;
  • REST create and update, through the shared wallet endpoint examples;
  • the serializer;
  • GraphQL create, update and clear.

🤖 Generated with Claude Code

@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 0b09d51 to a4170a5 Compare October 5, 2026 12:43
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch 2 times, most recently from e5b877b to 9778264 Compare October 5, 2026 16:38
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 9778264 to 18a45d6 Compare October 6, 2026 09:26
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 18a45d6 to 5834f99 Compare October 7, 2026 10:47
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 5834f99 to 3b18c17 Compare October 7, 2026 12:39
@annvelents
annvelents added this pull request to stack #6187 October 8, 2026 11:15
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 3b18c17 to fc671a8 Compare October 26, 2026 11:42
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from fc671a8 to bff8b33 Compare October 26, 2026 11:56
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from bff8b33 to 5a2a853 Compare October 26, 2026 19:22
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch 2 times, most recently from 68cbacf to 4df77bb Compare October 27, 2026 17:09
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 4df77bb to 820f429 Compare October 29, 2026 09:31
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 820f429 to 782d9a0 Compare October 30, 2026 13:52
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 782d9a0 to 737f1d7 Compare October 30, 2026 14:27
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 737f1d7 to 588ba80 Compare October 30, 2026 14:47
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 588ba80 to 08f4a81 Compare October 30, 2026 15:18
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 08f4a81 to b1f78ed Compare October 30, 2026 15:22
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from b1f78ed to 7648127 Compare October 30, 2026 18:49
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 7648127 to c62e1e6 Compare October 30, 2026 19:10
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch 2 times, most recently from a6d7c79 to 46948f2 Compare November 3, 2026 14:06
BIL-522: a wallet can carry its own payment term, which every top-up
invoice of that wallet follows ahead of the customer and billing entity
terms.

Invoices::PaidCreditService, which every top-up path goes through,
snapshots the wallet term with payment_term_source "wallet" when the
wallet has one, and otherwise resolves as before.

Wallet create and update validate and store the normalized term; null
clears it on update. The REST wallet endpoints, the wallet serializer
(and so the wallet webhooks) and the wallet GraphQL type and mutations
expose it.

Payment-gated top-ups keep moving both dates to the payment day on
finalization, as decided by product; a spec pins that behaviour.
@annvelents
annvelents force-pushed the feat-payment-terms-wallets branch from 46948f2 to b4ae309 Compare November 3, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant