fix(invoice): localize Shopify payment-gateway names on the PDF

Customer reported that on the German invoice PDF the payment method
showed up as 'Zahlart: Bank Deposit' while the order-confirmation page
on the storefront localized it correctly to 'Bank\u00fc1berweisung'. Cause:
Shopify's Admin GraphQL API only ever returns the *English* template
name in 'Order.paymentGatewayNames', even when the shop / order locale
is German \u2014 the localization happens client-side at checkout but is
NOT exposed via the API. So the PDF and the storefront naturally
diverge unless we mirror the translations ourselves.

Fix: introduce a per-language 'paymentGatewayLabels' map on
'InvoiceStrings' covering the built-in Shopify manual-payment
templates (Bank Deposit, Money Order, Cash on Delivery) plus the
standard non-manual gateways (Shopify Payments, PayPal, Klarna,
Sofort, Giropay, Bogus). 'prettifyGatewayName' now takes this map
and looks up the normalized key (lowercased, separators collapsed),
falling back to a title-cased rendering for unknown values.

DE result: 'Zahlart: Bank\u00fc1berweisung', 'Manuelle Zahlung', 'Nachnahme'.
EN result: unchanged.

New smoke assertions verify the DE PDF now shows 'Manuelle Zahlung'
for the AT B2B fixture's 'manual' gateway and that the raw English
'Manual' no longer appears next to the 'Zahlart' label.

Note on other Shopify-sourced strings on the PDF: 'shippingLine.title'
(e.g. 'Standard') is similarly merchant/locale-dependent, but unlike
gateway names it's fully customizable per-shop in Shopify Admin and
is not a fixed enum we can translate \u2014 left untouched pending an
explicit report. Product titles, discount codes and addresses are
likewise merchant-/customer-supplied and flow through verbatim by
design.
This commit is contained in:
Gerhard Scheikl
2026-05-15 16:08:19 +02:00
parent 2a4a7fd983
commit c24d567ae4
3 changed files with 67 additions and 17 deletions
+17 -17
View File
@@ -311,7 +311,7 @@ export function InvoiceDocument({ invoice }: DocProps) {
<View style={styles.metaRow}>
<Text style={styles.metaLabel}>{t.paymentMethodLabel}</Text>
<Text style={styles.metaValue}>
{invoice.paymentGatewayNames.map(prettifyGatewayName).join(", ")}
{invoice.paymentGatewayNames.map((n) => prettifyGatewayName(n, t.paymentGatewayLabels)).join(", ")}
</Text>
</View>
)}
@@ -643,24 +643,24 @@ function normaliseWebUrl(url: string): string {
/**
* Turn a Shopify payment-gateway machine name (e.g. `shopify_payments`,
* `manual`, `bogus`) into something a customer can read on the invoice. We
* keep this purely cosmetic — the underlying value is preserved for any
* downstream automation.
* `manual`, `bogus`) or a built-in manual-payment template name (e.g.
* `Bank Deposit`, `Money Order`) into the localized customer-facing label
* shown on the invoice. The Shopify Admin API only exposes English
* template names — see `InvoiceStrings.paymentGatewayLabels` for the
* rationale.
*
* Lookup is keyed on the *normalized* name (lowercased, separators
* collapsed). Unknown gateways fall back to a title-cased rendering
* of the raw name so we never silently print empty meta-rows.
*/
function prettifyGatewayName(name: string): string {
const known: Record<string, string> = {
manual: "Manual",
bogus: "Bogus (Test)",
shopify_payments: "Shopify Payments",
paypal: "PayPal",
cash_on_delivery: "Cash on delivery",
"cash-on-delivery": "Cash on delivery",
};
const key = name.trim().toLowerCase();
if (known[key]) return known[key];
// Replace separators and title-case each word.
function prettifyGatewayName(
name: string,
labels: Record<string, string>,
): string {
const key = name.trim().toLowerCase().replace(/[_\-]+/g, " ").replace(/\s+/g, " ");
if (labels[key]) return labels[key];
return key
.split(/[_\s-]+/)
.split(" ")
.filter(Boolean)
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(" ");