feat(core,react): add the payment link pay flow contract - #201
Open
TaprootFreak wants to merge 2 commits into
Open
feat(core,react): add the payment link pay flow contract#201TaprootFreak wants to merge 2 commits into
TaprootFreak wants to merge 2 commits into
Conversation
Contributor
Author
|
Took 2 review passes to reach a clean run. The second pass found seven places where the contract, as first written here, did not match what the
Note for downstream consumers: |
TaprootFreak
marked this pull request as ready for review
August 2, 2026 15:55
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 3 of #198: move the payment link pay-flow contract into the packages. CRUD was already
covered; this adds the part a paying terminal or wallet needs.
Changes
@dfx.swiss/core(definitions/route.ts,PaymentLinksApi)paymentLink/standard,paymentLink/standard/:id,paymentLink/payment/wait,paymentLink/payment/confirm,paymentLink/historyPaymentStandard,C2BPaymentMethod,TransferMethod,TransferAmount,PaymentAmount,PaymentQuote,PaymentLinkRequestBase,PaymentLinkPayRequest,PaymentLinkPayTerminal,PaymentLinkPayResponse,PaymentLinkHistory,PaymentLinkHistoryPayment, plus thePaymentLinkPaymentQuery/PaymentLinkHistoryQueryparameter shapeshasPaymentQuote()type guardgetStandards,getStandard,waitForPayment,confirmPayment,getHistory@dfx.swiss/reactre-exports all of it.Tests cover every new client method, the query building (comma-separated statuses, ISO dates,
omitted parameters) and both branches of the type guard.
What the API actually says
The issue asked to verify the endpoints before freezing them. Doing that turned up four places
where a locally modelled version of this contract does not match the API:
paymentLink/historyreturns payment links (id,routeId, url, lnurl, status, recipient, config …) each with their
payments[]andtotalCompletedAmount. It has notag,callback,quote,minSendable/maxSendableand norequestedAmount.PaymentLinkHistoryis modelled on the payment link, withpaymentomitted —the history carries the plural
paymentsinstead, never the singular one.note,mode,expiryDate,txCount,isConfirmed,urlandlnurl.paymentLink/payment/waitreturns the whole payment link, not a bare{ status }. The statuslives at
payment.status. (The bare status shape belongs to the LNURL wait endpoint, which is adifferent call.)
routeandcurrencyare optional on a pay request, andavailableis always present on atransfer amount.
paymentLink/standarddoes exist — it is served by its own controller rather than the payment linkcontroller, which is why it is easy to miss.
Scope
recipientreuses the existingPaymentLinkRecipientinstead of introducing a second recipienttype. It is the same object; a separate one would only differ by declaring the address fields as
required, which the API does not guarantee.
handling belong to the consumer. The parts worth sharing are the wire types and the URL and query
building, and those are now here.
storage, and the UI-only "no payment" pseudo status.
PaymentLink.id/routeIdare typed asstringwhile the API returns numbers.That predates this change and is left alone;
PaymentLinkHistoryinherits it rather thancontradicting its own base type.
Version fields, changelogs and lockfile pins are untouched, per CONTRIBUTING.
Corrections from review
Reviewing the frozen contract against the API turned up seven more differences, all fixed here:
PaymentLinkHistoryQuery.linkIdis gone. The history endpoint never reads it — a caller who passedit would silently get the account's whole history instead of one link's.
PaymentLinkPaymentQuery.routeis gone. Wait and confirm do not read it; only create and cancel do,and those are not wrapped here.
PaymentLinkHistoryPaymentandPaymentLinkgainedfrontendUrl, which the API always sends;PaymentLinkalso gained thewebhookUrlit returns. This reacheswaitForPayment,confirmPaymentand every history entry.PaymentLinkRecipientgained the five merchant fields the API carries in these responses(
registrationNumber,storeType,merchantCategory,goodsType,goodsCategory).TransferMethodnow covers the manually settled methods viaManualPaymentMethod. The API appendsTaprootAssetto every transfer amount list, and that value is in no blockchain enum — the oldunion rejected a value that arrives on every single response.
PaymentLinkPayTerminal.error,.messageand.statusCodeare required. They always arrivetogether; declaring them optional invited a null check that can never fire.
PaymentLinkgaining a requiredfrontendUrlis worth calling out for downstream consumers: readingcode is unaffected, but code that builds a
PaymentLinkliteral (test fixtures, mocks) has to add thefield.