Skip to content

docs(surcharge): add External Surcharge page - #215

Open
tinu-hareesswar wants to merge 8 commits into
mainfrom
docs/external-surcharge
Open

tinu-hareesswar wants to merge 8 commits into
mainfrom
docs/external-surcharge

Conversation

@tinu-hareesswar

Copy link
Copy Markdown
Contributor

Adds a new page, External Surcharge, under integration-guide/workflows/surcharge/, covering both halves of the recently launched feature:

  1. Delegating surcharge calculation to an external surcharge processor (InterPayments).
  2. Routing payments to different payment processors based on the surcharge returned.

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 center v1.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_amount in routing is 0, never null. The routing engine substitutes 0 when no external surcharge is attached to the payment, so an EQUAL TO 0 rule 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:

Active configuration Connector used
== 0 → fauxpay, > 0 → pretendpay fauxpay (rule 1 hit)
== 0 → pretendpay (mirrored) pretendpay (rule 1 hit, different target)
> 0 → pretendpay only phonypay (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 0 might not match a null value has been replaced with the real behaviour, along with the caveat that 0 also 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_details in 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-supplied surcharge_amount. A rule-based (internal) surcharge does take precedence over external, and that is now stated.
  • How the surcharge reaches checkout is now described concretelyPOST /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 returns 200 with surcharge_details: null and the payment proceeds unsurcharged rather than erroring.
  • The Profile field on the connector form is fixed by the Control Center's profile switcher, not selectable as the draft implied.

Still outstanding

Marked inline in the page and summarised in a "Known gap in this draft" section:

  • A screenshot of the payment form showing the surcharge appearing as a card number is entered.
  • A screenshot of a debit card producing no surcharge.
  • Screenshots of the Payments list showing the two routing branches splitting on a real surcharge value.

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

  • Whether the Developers → Payment Settings → Surcharge step matches the intended product flow, or whether selecting the connector is expected to happen automatically on connect.
  • Whether the EQUAL TO 0 caveat is worth calling out as prominently as it is, or whether it is better placed in the routing docs.
  • Tone and depth of the eligibility API description for a merchant-facing page — it may be more detail than belongs here.

tinu hareesswar and others added 5 commits August 20, 2026 16:11
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
@nfarah86 nfarah86 self-assigned this Sep 15, 2026
@nfarah86
nfarah86 marked this pull request as ready for review September 15, 2026 11:42
@nfarah86
nfarah86 requested a balanced review from Copilot September 17, 2026 15:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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:
@nfarah86

Copy link
Copy Markdown
Collaborator

@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

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.

3 participants