docs(surcharge): add External Surcharge page - #215
tinu-hareesswar wants to merge 8 commits into
Conversation
Adds integration-guide/workflows/surcharge/external-surcharge.md covering external surcharge via InterPayments and routing on surcharge_amount, and registers it under Surcharge in SUMMARY.md. The page is a draft: two screenshots (surcharge appearing on card entry, and the zero-surcharge debit card case) and the routing verification are marked TODO, as they need a live InterPayments API key that was not available. Not ready to publish as-is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RQiq7tufouNPH4Wn4s32gz
…t routing semantics
- Add the required "select the surcharge connector on the profile" step
(Developers -> Payment Settings -> Surcharge), without which external
surcharge silently does nothing, with a screenshot.
- Add a screenshot of InterPayments connected under Connectors ->
Surcharge Processor.
- Describe how the surcharge is actually fetched during checkout
(POST /payments/{payment_id}/eligibility, its auth, the 15-minute
cache, and its best-effort failure behaviour).
- Correct the routing guidance: surcharge_amount is presented to the
routing engine as 0, never null, so EQUAL TO 0 matches - but it also
matches when no surcharge was ever calculated. Replace the earlier
speculative null warning with this.
- Correct the FAQ on request-supplied surcharge_details: on a profile
with external surcharge enabled the external value replaces it, it
does not take precedence.
- Note that a rule-based surcharge takes precedence over external.
- Clarify that the Profile field on the connector form is fixed by the
profile switcher rather than selectable.
The two payment-form screenshots and the end-to-end routing split
remain outstanding; both are marked in the page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQiq7tufouNPH4Wn4s32gz
…ligibility gate Replaces the pending-screenshot TODOs with evidence captured against a live sandbox with InterPayments connected. - Add payment-detail screenshots for a surcharged payment (net_amount 103 USD, surcharge 3 USD, routed to Pretendpay) and a zero-surcharge payment (net_amount unchanged, routed to Fauxpay). - Add the Payments-list screenshot verifying the surcharge_amount routing rule splits traffic across the two processors. - Document that the checkout SDK only fetches the surcharge when the payment's sdk_next_action is eligibility_check; with "confirm" it never calls /eligibility even though the processor is connected and selected on the profile. Eligibility checks are enabled per merchant account, not self-serve. - Note that the surcharge decision comes from the card BIN, not from the payment_method_subtype the caller declares. - Note that confirming without an eligibility call silently produces no surcharge and routes down the EQUAL TO 0 branch. - Add an FAQ entry that self-hosted deployments need UCS for this feature. The shopper-facing payment form still could not be captured; the reason is now documented rather than left as an open TODO.
The previous image came from the local self-hosted stack: it showed merchant "Test merchant", an uncropped sidebar including the signed-in user, and the "default" profile, on a stack that cannot render external surcharge at all. Replaced with a capture from the hosted sandbox on the docs_ext_surcharge profile, cropped to the content column so no user identity, avatar, or account identifiers are visible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RQiq7tufouNPH4Wn4s32gz
Eligibility checks are now enabled on the sandbox merchant account, so
/payments/{id}/client returns sdk_next_action.next_action =
"eligibility_check" with should_block_confirm = true.
Selecting a saved card makes the SDK fire POST /payments/{id}/eligibility
and render the quoted surcharge on the payment form; that capture replaces
the placeholder claim that the form could not be shown at all. Typing a new
card number still fetches nothing on the SDK build tested, and blocks
confirm indefinitely, so that stays documented as a warning rather than as
an unqualified gap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQiq7tufouNPH4Wn4s32gz
There was a problem hiding this comment.
🟡 Changes recommended
Several compliance and fallback-routing statements are inaccurate or contradict the documented behavior.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds merchant documentation for configuring external surcharge processing and surcharge-based payment routing.
Changes:
- Documents InterPayments setup, eligibility calls, SDK behavior, and testing.
- Explains routing based on
surcharge_amount. - Adds the page to documentation navigation.
File summaries
| File | Description |
|---|---|
SUMMARY.md |
Adds External Surcharge navigation. |
integration-guide/workflows/surcharge/external-surcharge.md |
Adds the external surcharge guide. |
Review details
Suppressed comments (2)
integration-guide/workflows/surcharge/external-surcharge.md:35
- The table incorrectly says compliance responsibility is transferred to the external processor. The processor automates the surcharge decision, but the merchant still retains applicable legal and network responsibilities.
| Compliance responsibility | Yours | Handled by the surcharge processor |
integration-guide/workflows/surcharge/external-surcharge.md:240
- A payment reaching the third processor is not unambiguously a rule miss. Default Fallback is also used when a rule matches but its selected processor is ineligible or unavailable, so this test can misdiagnose connector eligibility as a condition failure.
Before testing, connect a third processor you are not routing to and move it to the top of your Default Fallback list. Then any payment landing on that third processor is unambiguously a rule miss, and anything landing on Fauxpay or Pretendpay is unambiguously a rule hit. Restore your fallback order afterwards.
- Files reviewed: 2/17 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| This gives you two things: | ||
|
|
||
| * **Compliance is maintained for you.** The surcharge processor keeps up with card-network and jurisdictional rules. Debit cards, for example, generally cannot be surcharged in the US — the processor returns a zero surcharge for them, without you writing a rule. |
| | Who decides the amount | You, via rules in the Surcharge Manager | The external surcharge processor | | ||
| | Compliance responsibility | Yours | Handled by the surcharge processor | | ||
| | Inputs | Payment parameters you choose (amount, currency, payment method, card network, …) | The card the shopper actually entered | | ||
| | Setup | Configure rules per profile | Connect the surcharge processor once | |
| Amounts are entered in the **smallest currency unit** — `100` means $1.00 for USD. | ||
|
|
||
| {% hint style="info" %} | ||
| Rules are evaluated top to bottom and the first match wins. Here the two conditions are mutually exclusive and between them cover every possible value, so the order does not matter and no payment should reach your [Default Fallback](../intelligent-routing/default-fallback-routing.md) list. If payments *are* landing on your fallback processor, the rules are not matching — check the profile's surcharge connector setting. |
|
|
||
| ## What this page was verified against | ||
|
|
||
| Everything on this page was verified against a live sandbox with InterPayments connected and selected on the profile, and with eligibility checks enabled on the merchant account: |
|
@tinu-hareesswar I updated the PR because the HS codebase suggests something else with some of the claims here. I think we should reflect better what is hosted vs. self-hosted. If you can review the changes and update the PR with this in mind, I'll re-review, and can manage the merge myself |
Adds a new page, External Surcharge, under
integration-guide/workflows/surcharge/, covering both halves of the recently launched feature:Opened as a draft — two screenshots and one end-to-end verification are still outstanding (see below).
What was verified against a live stack
Written against a local Hyperswitch deployment (router
v1.126.0, control centerv1.38.7), with InterPayments connected as a surcharge processor using a real test API key. Everything in the setup half of the page was walked through in the Control Center; the routing semantics were verified with live payments.The profile-level step was missing from the original draft and is the most important addition. Connecting InterPayments under Connectors → Surcharge Processor is not sufficient — the profile also has to select it under Developers → Payment Settings → Surcharge. Without that, payments go through unsurcharged and nothing indicates why.
surcharge_amountin routing is0, never null. The routing engine substitutes0when no external surcharge is attached to the payment, so anEQUAL TO 0rule does match. This was confirmed by running identical payments against three routing configurations, with a third connector placed at the head of the Default Fallback list so a rule hit and a rule miss were distinguishable:== 0 → fauxpay,> 0 → pretendpayfauxpay(rule 1 hit)== 0 → pretendpay(mirrored)pretendpay(rule 1 hit, different target)> 0 → pretendpayonlyphonypay(no rule matched, fell through to fallback)The mirrored configuration rules out coincidence, and the third case confirms a rule miss looks different from a rule hit. The earlier draft's speculative warning that
EQUAL TO 0might not match a null value has been replaced with the real behaviour, along with the caveat that0also means "no surcharge was ever calculated" — so the zero branch is not proof of a zero-surcharge decision by the processor.Other corrections to the earlier draft
surcharge_detailsin payments/create does not take precedence. The original FAQ had this backwards. On a profile with external surcharge enabled, the external value replaces a merchant-suppliedsurcharge_amount. A rule-based (internal) surcharge does take precedence over external, and that is now stated.POST /payments/{payment_id}/eligibility, its auth (publishable key +client_secret, or a server-side secret key), the 15-minute cache, and the fact that the call is best-effort: a surcharge failure returns200withsurcharge_details: nulland the payment proceeds unsurcharged rather than erroring.Still outstanding
Marked inline in the page and summarised in a "Known gap in this draft" section:
These need an environment that can complete a live surcharge calculation end to end; the stack used here could not. The "Known gap" section is a note for reviewers and should be removed before publishing.
Review focus
EQUAL TO 0caveat is worth calling out as prominently as it is, or whether it is better placed in the routing docs.