Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@
"check:redirects": "node scripts/check-redirects.mjs",
"linkcheck:rendered": "node scripts/check-rendered-links.mjs",
"test": "vitest run",
"validate": "npm run format:check && npm run check:nimbus && npm run check && npm run lint && npm run test && npm run linkcheck && npm run test:worker",
"validate": "npm run format:check && npm run check:nimbus && npm run check:examples && npm run check && npm run lint && npm run test && npm run linkcheck && npm run test:worker",
"check:agent-docs": "node scripts/check-agent-docs.mjs",
"sync:agent-skills": "node scripts/sync-agent-skills.mjs",
"postbuild": "node scripts/publish-agent-discovery.mjs && npm run check:site",
"check:og-images": "node scripts/check-og-images.mjs",
"check:site": "node scripts/check-site.mjs",
"test:worker": "node --test tests/worker.test.mjs",
"lint:spelling": "typos src/content --config _typos.toml"
"lint:spelling": "typos src/content --config _typos.toml",
"check:examples": "node --test scripts/check-json-examples.test.mjs && node scripts/check-json-examples.mjs"
},
"devDependencies": {
"@astrojs/check": "^0.9.12",
Expand Down
57 changes: 57 additions & 0 deletions scripts/check-json-examples.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { glob, readFile } from "node:fs/promises";
import { mdxToMdast } from "satteri";

export function validateJsonExamples(source, path) {
const errors = [];
let checked = 0;
// Keep source line numbers while removing frontmatter from the MDX parser input.
const content = source.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, (header) =>
header.replace(/[^\n]/g, ""),
);
const validate = (value, line, kind) => {
checked++;
try {
JSON.parse(value);
} catch (error) {
errors.push(`${path}:${line}: invalid ${kind}: ${error.message}`);
}
};
const visit = (node) => {
if (node.type === "code") {
const line = (node.position?.start.line ?? 0) + 1;
if (node.lang === "json") validate(node.value, line, "JSON example");
if (
["bash", "sh", "shell"].includes(node.lang) &&
/\bcurl\b/.test(node.value)
) {
// Single-quoted data is literal JSON, unlike an interpolated shell string.
const bodies = node.value.matchAll(
/(?:^|\s)(?:-d|--data(?:-raw|-binary)?)\s+'([^']*)'/g,
);
for (const body of bodies) {
if (!["{", "["].includes(body[1].trimStart()[0])) continue;
const bodyLine =
line + node.value.slice(0, body.index).split("\n").length - 1;
validate(body[1], bodyLine, "curl JSON body");
}
}
}
node.children?.forEach(visit);
};
visit(mdxToMdast(content));
return { checked, errors };
}

if (import.meta.main) {
let checked = 0;
const errors = [];
for await (const path of glob("src/content/**/*.mdx")) {
const result = validateJsonExamples(await readFile(path, "utf8"), path);
checked += result.checked;
errors.push(...result.errors);
}
if (errors.length) throw new Error(errors.sort().join("\n"));
console.log(
`Validated ${checked} JSON examples and literal curl JSON bodies.`,
);
}
48 changes: 48 additions & 0 deletions scripts/check-json-examples.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { validateJsonExamples } from "./check-json-examples.mjs";

test("checks JSON inside MDX components and curl data, while allowing form data", () => {
const source = `---
title: Examples
---
<Tabs>
<TabItem label="JSON">

\`\`\`json
{"message":"A literal ... is valid","items":[1,2]}
\`\`\`

</TabItem>
</Tabs>

\`\`\`bash
curl https://example.com --data '{"amount":10}'
curl https://example.com -d 'grant_type=authorization_code'
\`\`\``;
assert.deepEqual(validateJsonExamples(source, "guide.mdx"), {
checked: 2,
errors: [],
});
});

test("reports malformed JSON fences at their source location", () => {
const result = validateJsonExamples(
'---\ntitle: Example\n---\n\n```json\n{"token":"saved" "customer_id":"123"}\n```',
"guide.mdx",
);
assert.equal(result.checked, 1);
assert.match(result.errors[0], /^guide\.mdx:6: invalid JSON example:/);
});

test("rejects trailing commas in literal curl payloads", () => {
const result = validateJsonExamples(
'```bash\ncurl https://example.com \\\n -d \'{"total_amount":{"value":1500,}}\'\n```',
"cloud-api.mdx",
);
assert.equal(result.checked, 1);
assert.match(
result.errors[0],
/^cloud-api\.mdx:\d+: invalid curl JSON body:/,
);
});
8 changes: 4 additions & 4 deletions src/content/docs/get-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Choose the payment experience you want to build, then follow its integration gui
| Offer an accelerated wallet checkout | [Swift Checkout](/online-payments/checkouts/swift-checkout/) — add Apple Pay and Google Pay to your website. |
| Add payments to an e-commerce platform | [Plugins](/online-payments/plugins/) — use an integration for WooCommerce, PrestaShop, Wix, Medusa, or Vendure. |

For a first website integration, start with Hosted Checkout. Choose the Payment Widget when you need an embedded form. To charge returning customers, build on the widget with [saved cards and recurring payments](/online-payments/guides/tokenization-with-payment-sdk/).
For a first website integration, follow the [online payment quickstart](/online-payments/quickstart/) with Hosted Checkout. Choose the Payment Widget when you need an embedded form. To charge returning customers, build on the widget with [saved cards and recurring payments](/online-payments/guides/saved-cards/).

### Accept payments in person

Expand All @@ -34,11 +34,11 @@ Follow the [iOS quickstart](/terminal-payments/sdks/ios/quickstart/) or [Android

## Set up a sandbox

[Create a developer account](https://me.sumup.com/signup?signup_intent=developer) to start with a sandbox merchant account. If you already have a SumUp account, [create a sandbox merchant in Developer Settings](/online-payments/#getting-a-sandbox-merchant-account).
[Create a developer account](https://me.sumup.com/signup?signup_intent=developer) to start with a sandbox merchant account. If you already have a SumUp account, [create a sandbox merchant in Developer Settings](/tools/sandbox/).

Use the sandbox merchant’s credentials and merchant code when testing. For native SDK login, follow the [merchant account setup guide](/terminal-payments/quickstart/#users-with-multiple-merchant-accounts) to make sure you sign in to the sandbox merchant.
Use the sandbox merchant’s credentials and merchant code when testing. For native SDK login, follow the [merchant account setup guide](/tools/sandbox/#choose-the-account-for-native-sdk-login) to make sure you sign in to the sandbox merchant.

You can try Cloud API pairing and payments without a physical reader using [Virtual Solo](/terminal-payments/cloud-api/#solo-virtual-terminal).
You can try Cloud API pairing and payments without a physical reader using [Virtual Solo](/terminal-payments/cloud-api/testing/#solo-virtual-terminal).

## Choose your credentials

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/online-payments/3ds.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Common exemption categories include:
| Recurring payment | SCA is normally required when the customer establishes or changes a series of payments for the same amount and recipient. Later payments in that series can qualify for an exemption. |
| Trusted beneficiary | A customer can designate a business as trusted through a service offered by their issuer. The issuer controls the list and decides whether to apply the exemption. |

Some payments are outside the scope of SCA rather than exempt. For example, a properly established merchant-initiated transaction can be out of scope when it is made without the customer actively participating. It must be linked to the customer's original consent and authenticated setup. See [Tokenization and recurring payments](/online-payments/guides/tokenization-with-payment-sdk/) for the supported SumUp flow.
Some payments are outside the scope of SCA rather than exempt. For example, a properly established merchant-initiated transaction can be out of scope when it is made without the customer actively participating. It must be linked to the customer's original consent and authenticated setup. See [Tokenization and recurring payments](/online-payments/guides/saved-cards/) for the supported SumUp flow.

Thresholds and eligibility differ by market and can change. Do not use the categories above as legal advice or implement exemption rules in your frontend.

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/online-payments/apm/apple-pay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ In this guide, you will learn how to directly integrate Apple Pay with SumUp, so
- You have a SumUp merchant account and have already filled in your [account details](https://me.sumup.com/account).
- Get familiar with [Apple Pay on the Web guide](https://developer.apple.com/documentation/apple_pay_on_the_web).
- Offering Apple Pay requires registering with Apple on all web domains that will expose an Apple Pay button (includes TLD and subdomains). This is a requirement for production AND test environments.
- If you want to test payments without involving real funds, [create a sandbox merchant account](/online-payments/#getting-a-sandbox-merchant-account).
- If you want to test payments without involving real funds, [create a sandbox merchant account](/tools/sandbox/).
- Complete the domain onboarding setup steps described in your Dashboard under **Settings** > **For developers** > **Payment wallets**.

<Image
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/online-payments/apm/google-pay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ In this guide, you will learn how to directly integrate Google Pay with SumUp. P
## Prerequisites

- You have a SumUp merchant account and have already filled in your [account details](https://me.sumup.com/account).
- If you want to test payments without involving real funds, [create a sandbox merchant account](/online-payments/#getting-a-sandbox-merchant-account).
- If you want to test payments without involving real funds, [create a sandbox merchant account](/tools/sandbox/).
- Review [Google Pay API terms of service](https://payments.developers.google.com/terms/sellertos).
- Complete the domain onboarding setup steps described in your Dashboard under **Settings** > **For developers** > **Payment wallets**. You can read Google's tutorial [Google Pay for Payments](https://developers.google.com/pay/api/web/guides/tutorial), which covers the requirements you're expected to follow in order to successfully offer this payment method.

Expand Down
4 changes: 1 addition & 3 deletions src/content/docs/online-payments/apm/integration-guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,14 @@ In the Redirect Flow, when the checkout is processed, you receive the `"status":

```json
{
...
"status": "pending",
"next_step": {
"url": "https://apm-redirect-link",
"method": "POST",
"payload": {
"....": "..."
}
},
...
}
}
```

Expand Down
2 changes: 2 additions & 0 deletions src/content/docs/online-payments/checkouts/card-widget.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Strong Customer Authentication (SCA) may apply to European online payments. The

</Steps>

Use `onResponse` to refresh the order status from your backend. Before fulfillment, [verify the checkout and update the order once](/online-payments/guides/confirm-payment/). Logging a callback or showing a success message in the browser does not update or verify your backend order.

## Configurations

The Payment Widget allows you to customize certain properties on the payment form, as listed below. To override the default settings, add your own configuration property-value pairs to the `SumUpCard.mount` method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ This is the lowest-effort way to launch an online checkout while keeping payment
width="80%"
/>

New to online payments? Follow the [Hosted Checkout quickstart](/online-payments/quickstart/) for a complete sandbox payment before configuring your website.

## Prerequisites

- A [sandbox merchant account](/online-payments/#getting-a-sandbox-merchant-account) for test payments.
- A [sandbox merchant account](/tools/sandbox/) for test payments.
- Ability to [create a checkout](/api/checkouts/create) through the SumUp API.
- Authentication via API key or access token. See the [Authorization Guide](/tools/authorization/).
- A server-side integration to create the checkout and keep credentials secret.
Expand All @@ -31,7 +33,7 @@ This is the lowest-effort way to launch an online checkout while keeping payment
1. Create a checkout and set `hosted_checkout.enabled` to `true`.
2. Store the returned checkout `id` and `hosted_checkout_url`.
3. Redirect the customer to `hosted_checkout_url`, or share that URL in the flow you are building.
4. After the customer completes the payment, verify the final result through the API or with [webhooks](/online-payments/webhooks/).
4. After the customer completes payment, [retrieve and verify the checkout on your server](/online-payments/guides/confirm-payment/). Use webhooks to trigger that verification even if the customer closes the browser.

<Callout type="note" title="Note">
A Hosted Checkout session is available for 30 minutes. After that, unpaid sessions show an expired or not found page.
Expand Down Expand Up @@ -216,6 +218,8 @@ If you want the success page to include a button back to your website, set `redi

If `redirect_url` is present, the success page shows a button that sends the customer back to your website.

To receive server notifications as well, set `return_url` to your public HTTPS webhook endpoint. It is separate from the browser-facing `redirect_url`. Follow the [payment confirmation guide](/online-payments/guides/confirm-payment/).

## Handle Payment Outcomes

Hosted Checkout covers the customer-facing flow from payment page to final status page. Depending on the outcome, customers see different pages:
Expand Down
Loading
Loading