medusa-payment-sumup 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ const widgetModule = { widgets: [] };
3
+ const routeModule = {
4
+ routes: []
5
+ };
6
+ const menuItemModule = {
7
+ menuItems: []
8
+ };
9
+ const formModule = { customFields: {} };
10
+ const displayModule = {
11
+ displays: {}
12
+ };
13
+ const i18nModule = { resources: {} };
14
+ const plugin = {
15
+ widgetModule,
16
+ routeModule,
17
+ menuItemModule,
18
+ formModule,
19
+ displayModule,
20
+ i18nModule
21
+ };
22
+ module.exports = plugin;
@@ -0,0 +1,23 @@
1
+ const widgetModule = { widgets: [] };
2
+ const routeModule = {
3
+ routes: []
4
+ };
5
+ const menuItemModule = {
6
+ menuItems: []
7
+ };
8
+ const formModule = { customFields: {} };
9
+ const displayModule = {
10
+ displays: {}
11
+ };
12
+ const i18nModule = { resources: {} };
13
+ const plugin = {
14
+ widgetModule,
15
+ routeModule,
16
+ menuItemModule,
17
+ formModule,
18
+ displayModule,
19
+ i18nModule
20
+ };
21
+ export {
22
+ plugin as default
23
+ };
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SumUpClient = void 0;
4
+ const types_1 = require("../types");
5
+ const SUMUP_API_BASE = "https://api.sumup.com";
6
+ const REQUEST_TIMEOUT_MS = 10_000;
7
+ const MAX_RETRIES = 3;
8
+ // Use zero-delay backoff in tests so the suite stays fast.
9
+ const BASE_BACKOFF_MS = process.env.NODE_ENV === "test" ? 0 : 300;
10
+ // Fields that must never appear in logged/thrown error messages.
11
+ const SENSITIVE_FIELDS = new Set([
12
+ "card",
13
+ "cvv",
14
+ "pan",
15
+ "pan_fingerprint",
16
+ "token",
17
+ "number",
18
+ "secret",
19
+ ]);
20
+ /** Strips sensitive keys from a JSON error body before it appears in error messages. */
21
+ function redactErrorBody(text) {
22
+ try {
23
+ const obj = JSON.parse(text);
24
+ for (const key of SENSITIVE_FIELDS) {
25
+ delete obj[key];
26
+ }
27
+ return JSON.stringify(obj);
28
+ }
29
+ catch {
30
+ // Non-JSON body (e.g. HTML gateway error) — truncate to avoid noise.
31
+ return text.length > 500 ? `${text.slice(0, 500)}…` : text;
32
+ }
33
+ }
34
+ /** Classify an HTTP status code into a SumUpErrorType. */
35
+ function classifyStatus(status) {
36
+ if (status === 400 || status === 422)
37
+ return "validation";
38
+ if (status === 401 || status === 403)
39
+ return "auth";
40
+ if (status === 404)
41
+ return "not_found";
42
+ if (status === 409)
43
+ return "conflict";
44
+ if (status === 429)
45
+ return "rate_limit";
46
+ return "server"; // 5xx and any other non-OK code
47
+ }
48
+ /** Async sleep. Resolves immediately when ms ≤ 0. */
49
+ function sleep(ms) {
50
+ if (ms <= 0)
51
+ return Promise.resolve();
52
+ return new Promise((resolve) => setTimeout(resolve, ms));
53
+ }
54
+ /**
55
+ * Compute how long to wait before the next retry.
56
+ * Respects the `Retry-After` header (seconds) when provided by the server,
57
+ * otherwise uses bounded exponential back-off with ±30 % jitter.
58
+ */
59
+ function backoffMs(attempt, retryAfterSec) {
60
+ if (retryAfterSec !== undefined && !Number.isNaN(retryAfterSec)) {
61
+ return Math.min(retryAfterSec * 1_000, 60_000);
62
+ }
63
+ const base = BASE_BACKOFF_MS * 2 ** attempt;
64
+ const jitter = Math.random() * base * 0.3;
65
+ return Math.min(base + jitter, 10_000);
66
+ }
67
+ class SumUpClient {
68
+ constructor(options) {
69
+ this.headers = {
70
+ Authorization: `Bearer ${options.apiKey}`,
71
+ "Content-Type": "application/json",
72
+ };
73
+ }
74
+ /**
75
+ * Central request dispatcher. Handles timeouts, error classification,
76
+ * and retries (with back-off) for transient failures (5xx, rate-limit,
77
+ * network errors, and timeouts).
78
+ */
79
+ async request(url, init, attempt = 0) {
80
+ let res;
81
+ // ── Network / timeout ──────────────────────────────────────────
82
+ try {
83
+ res = await fetch(url, {
84
+ ...init,
85
+ headers: this.headers,
86
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
87
+ });
88
+ }
89
+ catch (err) {
90
+ const isTimeout = err?.name === "TimeoutError" || err?.name === "AbortError";
91
+ const type = isTimeout ? "timeout" : "network";
92
+ if (attempt < MAX_RETRIES) {
93
+ await sleep(backoffMs(attempt));
94
+ return this.request(url, init, attempt + 1);
95
+ }
96
+ throw new types_1.SumUpClientError(isTimeout
97
+ ? `SumUp request timed out after ${REQUEST_TIMEOUT_MS}ms`
98
+ : `SumUp network error: ${err?.message ?? "unknown"}`, type);
99
+ }
100
+ // ── Success ────────────────────────────────────────────────────
101
+ if (res.ok) {
102
+ if (res.status === 204)
103
+ return undefined;
104
+ return (await res.json());
105
+ }
106
+ // ── Error response ─────────────────────────────────────────────
107
+ const bodyText = await res.text().catch(() => "");
108
+ const type = classifyStatus(res.status);
109
+ const isRetryable = type === "rate_limit" || type === "server";
110
+ if (isRetryable && attempt < MAX_RETRIES) {
111
+ const retryAfterHeader = res.headers.get("Retry-After");
112
+ const retryAfterSec = retryAfterHeader != null
113
+ ? parseInt(retryAfterHeader, 10)
114
+ : undefined;
115
+ await sleep(backoffMs(attempt, retryAfterSec));
116
+ return this.request(url, init, attempt + 1);
117
+ }
118
+ throw new types_1.SumUpClientError(`SumUp API error (${res.status}): ${redactErrorBody(bodyText)}`, type, res.status);
119
+ }
120
+ async createCheckout(params) {
121
+ return this.request(`${SUMUP_API_BASE}/v0.1/checkouts`, {
122
+ method: "POST",
123
+ body: JSON.stringify(params),
124
+ });
125
+ }
126
+ async getCheckout(checkoutId) {
127
+ return this.request(`${SUMUP_API_BASE}/v0.1/checkouts/${encodeURIComponent(checkoutId)}`, { method: "GET" });
128
+ }
129
+ async deactivateCheckout(checkoutId) {
130
+ try {
131
+ await this.request(`${SUMUP_API_BASE}/v0.1/checkouts/${encodeURIComponent(checkoutId)}`, { method: "DELETE" });
132
+ }
133
+ catch (err) {
134
+ // A 404 means the checkout is already gone — treat as success.
135
+ if (err instanceof types_1.SumUpClientError && err.type === "not_found")
136
+ return;
137
+ throw err;
138
+ }
139
+ }
140
+ async refundTransaction(transactionId, amount) {
141
+ const body = {};
142
+ if (amount !== undefined)
143
+ body.amount = amount;
144
+ return this.request(`${SUMUP_API_BASE}/v0.1/me/refund/${encodeURIComponent(transactionId)}`, { method: "POST", body: JSON.stringify(body) });
145
+ }
146
+ }
147
+ exports.SumUpClient = SumUpClient;
148
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VtdXAtY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL3Byb3ZpZGVycy9zdW11cC9jb3JlL3N1bXVwLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFNQSxvQ0FBNEM7QUFFNUMsTUFBTSxjQUFjLEdBQUcsdUJBQXVCLENBQUM7QUFDL0MsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUM7QUFDbEMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0FBRXRCLDJEQUEyRDtBQUMzRCxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0FBRWxFLGlFQUFpRTtBQUNqRSxNQUFNLGdCQUFnQixHQUFHLElBQUksR0FBRyxDQUFDO0lBQy9CLE1BQU07SUFDTixLQUFLO0lBQ0wsS0FBSztJQUNMLGlCQUFpQjtJQUNqQixPQUFPO0lBQ1AsUUFBUTtJQUNSLFFBQVE7Q0FDVCxDQUFDLENBQUM7QUFFSCx3RkFBd0Y7QUFDeEYsU0FBUyxlQUFlLENBQUMsSUFBWTtJQUNuQyxJQUFJLENBQUM7UUFDSCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBNEIsQ0FBQztRQUN4RCxLQUFLLE1BQU0sR0FBRyxJQUFJLGdCQUFnQixFQUFFLENBQUM7WUFDbkMsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbEIsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AscUVBQXFFO1FBQ3JFLE9BQU8sSUFBSSxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLEdBQUcs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
@@ -0,0 +1,489 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const sumup_client_1 = require("./sumup-client");
5
+ const types_1 = require("../types");
6
+ // ISO-4217: 3 uppercase alpha characters.
7
+ const CURRENCY_RE = /^[A-Z]{3}$/;
8
+ const SUMUP_STATUS_MAP = {
9
+ PENDING: utils_1.PaymentSessionStatus.PENDING,
10
+ PAID: utils_1.PaymentSessionStatus.CAPTURED,
11
+ FAILED: utils_1.PaymentSessionStatus.ERROR,
12
+ EXPIRED: utils_1.PaymentSessionStatus.CANCELED,
13
+ };
14
+ class SumUpProviderService extends utils_1.AbstractPaymentProvider {
15
+ static validateOptions(options) {
16
+ if (!options.apiKey || typeof options.apiKey !== "string") {
17
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "SumUp API key (apiKey) is required.");
18
+ }
19
+ if (!options.merchantCode || typeof options.merchantCode !== "string") {
20
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "SumUp merchant code (merchantCode) is required.");
21
+ }
22
+ if (!options.medusaUrl || typeof options.medusaUrl !== "string") {
23
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Medusa backend URL (medusaUrl) is required.");
24
+ }
25
+ if (!options.redirectUrl || typeof options.redirectUrl !== "string") {
26
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Redirect URL (redirectUrl) is required.");
27
+ }
28
+ }
29
+ constructor(container, options) {
30
+ super(container, options);
31
+ this.logger_ = container.logger;
32
+ this.options_ = options;
33
+ this.debug_ =
34
+ options.debug ||
35
+ process.env.NODE_ENV === "development" ||
36
+ process.env.NODE_ENV === "test" ||
37
+ false;
38
+ this.client_ = new sumup_client_1.SumUpClient(options);
39
+ }
40
+ // ── Input validators ──────────────────────────────────────────────────────
41
+ /**
42
+ * Asserts that `amount` is a positive finite number and returns it as a
43
+ * parsed float. Throws INVALID_DATA otherwise.
44
+ */
45
+ static assertAmount(amount, ctx) {
46
+ const n = parseFloat(String(amount));
47
+ if (!Number.isFinite(n) || n <= 0) {
48
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `${ctx}: amount must be a positive finite number, got "${amount}"`);
49
+ }
50
+ return n;
51
+ }
52
+ /**
53
+ * Asserts that `code` is a valid ISO-4217 format (3 alpha chars), normalises
54
+ * to uppercase, and returns it. Throws INVALID_DATA otherwise.
55
+ */
56
+ static assertCurrency(code, ctx) {
57
+ const upper = typeof code === "string" ? code.toUpperCase().trim() : "";
58
+ if (!CURRENCY_RE.test(upper)) {
59
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `${ctx}: currency_code must be a 3-letter ISO-4217 code, got "${code}"`);
60
+ }
61
+ return upper;
62
+ }
63
+ /**
64
+ * Asserts that `id` is a non-empty string and returns it trimmed.
65
+ * Throws INVALID_DATA otherwise.
66
+ */
67
+ static assertExternalId(id, ctx) {
68
+ if (typeof id !== "string" || id.trim() === "") {
69
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `${ctx}: checkout ID is required`);
70
+ }
71
+ return id.trim();
72
+ }
73
+ /**
74
+ * Translates a SumUpClientError into the most appropriate MedusaError type
75
+ * so callers receive structured, actionable errors rather than raw HTTP
76
+ * status text. Always throws.
77
+ */
78
+ rethrowAsMedusaError(error, ctx) {
79
+ if (error instanceof types_1.SumUpClientError) {
80
+ switch (error.type) {
81
+ case "auth":
82
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, `SumUp authentication failed in ${ctx}. Verify your API key.`);
83
+ case "not_found":
84
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, `SumUp resource not found in ${ctx}.`);
85
+ case "validation":
86
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `SumUp rejected the request in ${ctx}: ${error.message}`);
87
+ case "conflict":
88
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, `SumUp conflict in ${ctx}: ${error.message}`);
89
+ case "rate_limit":
90
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `SumUp rate limit exceeded in ${ctx}. Please retry later.`);
91
+ // network / timeout / server: re-throw as-is with a clear prefix
92
+ default: {
93
+ const wrapped = new Error(`SumUp ${error.type} error in ${ctx}: ${error.message}`);
94
+ throw wrapped;
95
+ }
96
+ }
97
+ }
98
+ throw error;
99
+ }
100
+ /**
101
+ * Creates a new SumUp checkout for the payment session.
102
+ */
103
+ async initiatePayment(input) {
104
+ const ctx = "initiatePayment";
105
+ // ── Input validation ────────────────────────────────────────────
106
+ const amount = SumUpProviderService.assertAmount(input.amount, ctx);
107
+ const currency = SumUpProviderService.assertCurrency(input.currency_code, ctx);
108
+ const { context } = input;
109
+ try {
110
+ const checkoutReference = input.data?.session_id ?? crypto.randomUUID();
111
+ if (!input.data?.session_id) {
112
+ this.logger_.warn("SumUp: data.session_id is missing — using random UUID as checkout_reference. " +
113
+ "Webhook-based status updates may not work correctly.");
114
+ }
115
+ const webhookUrl = this.options_.medusaUrl + "/hooks/payment/sumup_sumup";
116
+ const customer = context?.customer;
117
+ // Merge guest data (from cart shipping address) with logged-in customer
118
+ // context; context.customer takes precedence when present.
119
+ const pdFirst = customer?.first_name ?? input.data?.first_name;
120
+ const pdLast = customer?.last_name ?? input.data?.last_name;
121
+ const pdEmail = customer?.email ?? input.data?.email;
122
+ const personalDetails = pdFirst || pdLast || pdEmail
123
+ ? {
124
+ ...(pdFirst && { first_name: pdFirst }),
125
+ ...(pdLast && { last_name: pdLast }),
126
+ ...(pdEmail && { email: pdEmail }),
127
+ }
128
+ : undefined;
129
+ const checkout = await this.client_.createCheckout({
130
+ checkout_reference: checkoutReference,
131
+ amount,
132
+ currency,
133
+ merchant_code: this.options_.merchantCode,
134
+ description: input.data?.description || "SumUp payment via Medusa",
135
+ return_url: webhookUrl,
136
+ redirect_url: this.options_.redirectUrl,
137
+ ...(personalDetails && { personal_details: personalDetails }),
138
+ });
139
+ this.debug_ &&
140
+ this.logger_.info(`SumUp checkout ${checkout.id} created with amount ${amount} ${currency}`);
141
+ return {
142
+ id: checkout.id,
143
+ data: {
144
+ id: checkout.id,
145
+ checkout_reference: checkoutReference,
146
+ idempotency_key: context?.idempotency_key,
147
+ },
148
+ };
149
+ }
150
+ catch (error) {
151
+ this.logger_.error(`Error in ${ctx}: ${error.message}`);
152
+ this.rethrowAsMedusaError(error, ctx);
153
+ }
154
+ }
155
+ /**
156
+ * Checks the SumUp checkout status to authorize the payment.
157
+ * SumUp auto-captures — when the checkout is PAID we return AUTHORIZED
158
+ * so that Medusa can proceed to capture (which is a no-op verification).
159
+ */
160
+ async authorizePayment(input) {
161
+ const ctx = "authorizePayment";
162
+ const externalId = SumUpProviderService.assertExternalId(input.data?.id, ctx);
163
+ try {
164
+ const checkout = await this.client_.getCheckout(externalId);
165
+ const sumupStatus = checkout.status;
166
+ const transaction = checkout.transactions?.[0];
167
+ switch (sumupStatus) {
168
+ case "PAID":
169
+ // SumUp auto-captures. Return AUTHORIZED so Medusa's
170
+ // authorize → capture flow proceeds naturally.
171
+ this.debug_ &&
172
+ this.logger_.info(`SumUp payment ${externalId} is PAID — returning AUTHORIZED`);
173
+ return {
174
+ data: {
175
+ ...input.data,
176
+ transaction_id: transaction?.id,
177
+ transaction_code: transaction?.transaction_code,
178
+ },
179
+ status: utils_1.PaymentSessionStatus.AUTHORIZED,
180
+ };
181
+ case "PENDING":
182
+ this.debug_ &&
183
+ this.logger_.info(`SumUp payment ${externalId} is still PENDING`);
184
+ return {
185
+ data: input.data,
186
+ status: utils_1.PaymentSessionStatus.PENDING,
187
+ };
188
+ case "FAILED":
189
+ return {
190
+ data: input.data,
191
+ status: utils_1.PaymentSessionStatus.ERROR,
192
+ };
193
+ case "EXPIRED":
194
+ return {
195
+ data: input.data,
196
+ status: utils_1.PaymentSessionStatus.CANCELED,
197
+ };
198
+ default:
199
+ this.logger_.warn(`SumUp payment ${externalId} returned unknown status "${sumupStatus}" — treating as PENDING`);
200
+ return {
201
+ data: input.data,
202
+ status: utils_1.PaymentSessionStatus.PENDING,
203
+ };
204
+ }
205
+ }
206
+ catch (error) {
207
+ this.logger_.error(`Error in ${ctx} for ${externalId}: ${error.message}`);
208
+ this.rethrowAsMedusaError(error, ctx);
209
+ }
210
+ }
211
+ /**
212
+ * SumUp auto-captures payments. This verifies the checkout is PAID.
213
+ */
214
+ async capturePayment(input) {
215
+ const ctx = "capturePayment";
216
+ const externalId = SumUpProviderService.assertExternalId(input.data?.id, ctx);
217
+ try {
218
+ const checkout = await this.client_.getCheckout(externalId);
219
+ if (checkout.status !== "PAID") {
220
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, `Cannot capture: SumUp checkout status is ${checkout.status}, not PAID.`);
221
+ }
222
+ const transactions = checkout.transactions ?? [];
223
+ if (transactions.length === 0) {
224
+ // PAID but no transactions array — log a warning and proceed;
225
+ // SumUp sometimes omits this on freshly completed checkouts.
226
+ this.logger_.warn(`SumUp checkout ${externalId} is PAID but has no transactions array. ` +
227
+ "transaction_id will not be available.");
228
+ return { data: { ...input.data } };
229
+ }
230
+ const transaction = transactions[0];
231
+ this.debug_ &&
232
+ this.logger_.info(`SumUp payment ${externalId} capture confirmed`);
233
+ return {
234
+ data: {
235
+ ...input.data,
236
+ transaction_id: transaction.id,
237
+ transaction_code: transaction.transaction_code,
238
+ },
239
+ };
240
+ }
241
+ catch (error) {
242
+ this.logger_.error(`Error in ${ctx} for ${externalId}: ${error.message}`);
243
+ this.rethrowAsMedusaError(error, ctx);
244
+ }
245
+ }
246
+ /**
247
+ * Refunds a captured SumUp payment.
248
+ */
249
+ async refundPayment(input) {
250
+ const ctx = "refundPayment";
251
+ // Validate refund amount first — before any API call.
252
+ const refundAmount = SumUpProviderService.assertAmount(input.amount, ctx);
253
+ const transactionId = input.data?.transaction_id;
254
+ if (!transactionId) {
255
+ // If we don't have transaction_id yet, try to get it from the checkout
256
+ const externalId = input.data?.id;
257
+ if (externalId) {
258
+ let checkout;
259
+ try {
260
+ checkout = await this.client_.getCheckout(externalId);
261
+ }
262
+ catch (error) {
263
+ this.logger_.error(`Error in ${ctx} looking up checkout ${externalId}: ${error.message}`);
264
+ this.rethrowAsMedusaError(error, ctx);
265
+ }
266
+ const transactions = checkout.transactions ?? [];
267
+ if (transactions.length === 0) {
268
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Cannot refund: checkout ${externalId} has no completed transactions.`);
269
+ }
270
+ const txn = transactions[0];
271
+ // Guard against refunding more than was originally captured.
272
+ if (typeof txn.amount === "number" &&
273
+ refundAmount > txn.amount) {
274
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Cannot refund ${refundAmount}: exceeds captured transaction amount of ${txn.amount}.`);
275
+ }
276
+ try {
277
+ await this.client_.refundTransaction(txn.id, refundAmount);
278
+ }
279
+ catch (error) {
280
+ this.logger_.error(`Error in ${ctx} refunding transaction ${txn.id}: ${error.message}`);
281
+ this.rethrowAsMedusaError(error, ctx);
282
+ }
283
+ this.debug_ &&
284
+ this.logger_.info(`SumUp refund for payment ${externalId} created with amount ${refundAmount}`);
285
+ return {
286
+ data: {
287
+ ...input.data,
288
+ transaction_id: txn.id,
289
+ },
290
+ };
291
+ }
292
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Cannot refund: no transaction_id found in payment data.");
293
+ }
294
+ try {
295
+ await this.client_.refundTransaction(transactionId, refundAmount);
296
+ this.debug_ &&
297
+ this.logger_.info(`SumUp refund for transaction ${transactionId} created with amount ${refundAmount}`);
298
+ return {
299
+ data: input.data,
300
+ };
301
+ }
302
+ catch (error) {
303
+ this.logger_.error(`Error in ${ctx} for transaction ${transactionId}: ${error.message}`);
304
+ this.rethrowAsMedusaError(error, ctx);
305
+ }
306
+ }
307
+ /**
308
+ * Cancels a pending SumUp checkout by deactivating it.
309
+ */
310
+ async cancelPayment(input) {
311
+ const externalId = input.data?.id;
312
+ try {
313
+ if (externalId) {
314
+ const checkout = await this.client_.getCheckout(externalId);
315
+ if (checkout.status === "EXPIRED") {
316
+ this.debug_ &&
317
+ this.logger_.info(`SumUp checkout ${externalId} is already expired, no need to cancel`);
318
+ return { data: input.data };
319
+ }
320
+ await this.client_.deactivateCheckout(externalId);
321
+ }
322
+ this.debug_ &&
323
+ this.logger_.info(`SumUp payment ${externalId} cancelled`);
324
+ return {
325
+ data: input.data,
326
+ };
327
+ }
328
+ catch (error) {
329
+ this.logger_.warn(`Could not cancel SumUp checkout ${externalId}: ${error.message}`);
330
+ return { data: input.data };
331
+ }
332
+ }
333
+ /**
334
+ * Deletes a payment session. Equivalent to cancellation for SumUp.
335
+ */
336
+ async deletePayment(input) {
337
+ return this.cancelPayment(input);
338
+ }
339
+ /**
340
+ * Retrieves the full checkout data from SumUp.
341
+ */
342
+ async retrievePayment(input) {
343
+ const ctx = "retrievePayment";
344
+ const externalId = SumUpProviderService.assertExternalId(input.data?.id, ctx);
345
+ try {
346
+ const data = await this.client_.getCheckout(externalId);
347
+ return { data: data };
348
+ }
349
+ catch (error) {
350
+ this.logger_.error(`Error in ${ctx} for ${externalId}: ${error.message}`);
351
+ this.rethrowAsMedusaError(error, ctx);
352
+ }
353
+ }
354
+ /**
355
+ * Maps SumUp checkout status to Medusa PaymentSessionStatus.
356
+ */
357
+ async getPaymentStatus(input) {
358
+ const ctx = "getPaymentStatus";
359
+ const externalId = SumUpProviderService.assertExternalId(input.data?.id, ctx);
360
+ try {
361
+ const checkout = await this.client_.getCheckout(externalId);
362
+ const status = checkout.status;
363
+ const mappedStatus = SUMUP_STATUS_MAP[status] ?? utils_1.PaymentSessionStatus.PENDING;
364
+ if (!SUMUP_STATUS_MAP[status]) {
365
+ this.logger_.warn(`SumUp checkout ${externalId} returned unknown status "${status}" — defaulting to PENDING`);
366
+ }
367
+ this.debug_ &&
368
+ this.logger_.debug(`SumUp checkout ${externalId} status: ${status} (mapped to: ${mappedStatus})`);
369
+ return { status: mappedStatus };
370
+ }
371
+ catch (error) {
372
+ this.logger_.error(`Error in ${ctx} for ${externalId}: ${error.message}`);
373
+ this.rethrowAsMedusaError(error, ctx);
374
+ }
375
+ }
376
+ /**
377
+ * SumUp does not support modifying a checkout after creation.
378
+ * We cancel the old checkout and create a new one with the updated amount.
379
+ */
380
+ async updatePayment(input) {
381
+ const ctx = "updatePayment";
382
+ // ── Input validation ────────────────────────────────────────────
383
+ const amount = SumUpProviderService.assertAmount(input.amount, ctx);
384
+ const currency = SumUpProviderService.assertCurrency(input.currency_code, ctx);
385
+ const externalId = input.data?.id;
386
+ try {
387
+ // Cancel the existing checkout (best-effort; may already be expired)
388
+ if (externalId) {
389
+ try {
390
+ await this.client_.deactivateCheckout(externalId);
391
+ }
392
+ catch {
393
+ // Ignore — checkout may already be in a terminal state
394
+ }
395
+ }
396
+ // Create a new checkout with the updated amount
397
+ const checkoutReference = input.data?.session_id ??
398
+ input.data?.checkout_reference ??
399
+ crypto.randomUUID();
400
+ const webhookUrl = this.options_.medusaUrl + "/hooks/payment/sumup_sumup";
401
+ const checkout = await this.client_.createCheckout({
402
+ checkout_reference: checkoutReference,
403
+ amount,
404
+ currency,
405
+ merchant_code: this.options_.merchantCode,
406
+ description: input.data?.description ||
407
+ "SumUp payment via Medusa",
408
+ return_url: webhookUrl,
409
+ redirect_url: this.options_.redirectUrl,
410
+ });
411
+ this.debug_ &&
412
+ this.logger_.info(`SumUp checkout updated: old=${externalId} → new=${checkout.id} (amount: ${amount} ${currency})`);
413
+ return {
414
+ data: {
415
+ id: checkout.id,
416
+ checkout_reference: checkoutReference,
417
+ idempotency_key: input.context?.idempotency_key,
418
+ },
419
+ };
420
+ }
421
+ catch (error) {
422
+ this.logger_.error(`Error in ${ctx} for ${externalId}: ${error.message}`);
423
+ this.rethrowAsMedusaError(error, ctx);
424
+ }
425
+ }
426
+ /**
427
+ * Processes SumUp webhook events.
428
+ * SumUp does NOT sign webhooks — we always verify by calling the API.
429
+ */
430
+ async getWebhookActionAndData(payload) {
431
+ const ctx = "getWebhookActionAndData";
432
+ const { data } = payload;
433
+ // Validate that the payload carries a checkout ID before hitting the API.
434
+ const checkoutId = data?.id;
435
+ if (!checkoutId || typeof checkoutId !== "string") {
436
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "SumUp webhook payload is missing a checkout ID.");
437
+ }
438
+ try {
439
+ // Always re-verify via the SumUp API — never trust the webhook body alone.
440
+ const checkout = await this.client_.getCheckout(checkoutId);
441
+ const session_id = checkout.checkout_reference;
442
+ const amount = new utils_1.BigNumber(checkout.amount);
443
+ const transaction = checkout.transactions?.[0];
444
+ const baseData = {
445
+ session_id,
446
+ amount,
447
+ transaction_id: transaction?.id,
448
+ transaction_code: transaction?.transaction_code,
449
+ };
450
+ switch (checkout.status) {
451
+ case "PAID":
452
+ return {
453
+ action: utils_1.PaymentActions.SUCCESSFUL,
454
+ data: baseData,
455
+ };
456
+ case "FAILED":
457
+ return {
458
+ action: utils_1.PaymentActions.FAILED,
459
+ data: baseData,
460
+ };
461
+ case "EXPIRED":
462
+ return {
463
+ action: utils_1.PaymentActions.CANCELED,
464
+ data: baseData,
465
+ };
466
+ case "PENDING":
467
+ return {
468
+ action: utils_1.PaymentActions.PENDING,
469
+ data: baseData,
470
+ };
471
+ default:
472
+ this.logger_.warn(`SumUp webhook: checkout ${checkoutId} has unknown status "${checkout.status}"`);
473
+ return {
474
+ action: utils_1.PaymentActions.NOT_SUPPORTED,
475
+ data: baseData,
476
+ };
477
+ }
478
+ }
479
+ catch (error) {
480
+ this.logger_.error(`Error in ${ctx} for checkout ${checkoutId}: ${error.message}`);
481
+ // Attempt graceful degradation: if we can classify the error (e.g. auth
482
+ // failure), surface it. Otherwise re-throw so Medusa can retry.
483
+ this.rethrowAsMedusaError(error, ctx);
484
+ }
485
+ }
486
+ }
487
+ SumUpProviderService.identifier = "sumup";
488
+ exports.default = SumUpProviderService;
489
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VtdXAtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3N1bXVwL2NvcmUvc3VtdXAtcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFLQSxxREFNbUM7QUFxQm5DLGlEQUE2QztBQUM3QyxvQ0FBNEM7QUFPNUMsMENBQTBDO0FBQzFDLE1BQU0sV0FBVyxHQUFHLFlBQVksQ0FBQztBQUVqQyxNQUFNLGdCQUFnQixHQUFzRDtJQUMxRSxPQUFPLEVBQUUsNEJBQW9CLENBQUMsT0FBTztJQUNyQyxJQUFJLEVBQUUsNEJBQW9CLENBQUMsUUFBUTtJQUNuQyxNQUFNLEVBQUUsNEJBQW9CLENBQUMsS0FBSztJQUNsQyxPQUFPLEVBQUUsNEJBQW9CLENBQUMsUUFBUTtDQUN2QyxDQUFDO0FBRUYsTUFBTSxvQkFBcUIsU0FBUSwrQkFBdUI7SUFReEQsTUFBTSxDQUFDLGVBQWUsQ0FBQyxPQUFnQztRQUNyRCxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxPQUFPLE9BQU8sQ0FBQyxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDMUQsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVksRUFDOUIscUNBQXFDLENBQ3RDLENBQUM7UUFDSixDQUFDO1FBQ0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLElBQUksT0FBTyxPQUFPLENBQUMsWUFBWSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RFLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLGlEQUFpRCxDQUNsRCxDQUFDO1FBQ0osQ0FBQztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxJQUFJLE9BQU8sT0FBTyxDQUFDLFNBQVMsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUNoRSxNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJB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
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const services_1 = require("./services");
5
+ const services = [services_1.SumUpProviderService];
6
+ exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.PAYMENT, {
7
+ services,
8
+ });
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3N1bXVwL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscURBQW9FO0FBQ3BFLHlDQUFrRDtBQUVsRCxNQUFNLFFBQVEsR0FBRyxDQUFDLCtCQUFvQixDQUFDLENBQUM7QUFFeEMsa0JBQWUsSUFBQSxzQkFBYyxFQUFDLGVBQU8sQ0FBQyxPQUFPLEVBQUU7SUFDN0MsUUFBUTtDQUNULENBQUMsQ0FBQyJ9
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SumUpProviderService = void 0;
7
+ var sumup_provider_1 = require("../core/sumup-provider");
8
+ Object.defineProperty(exports, "SumUpProviderService", { enumerable: true, get: function () { return __importDefault(sumup_provider_1).default; } });
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3N1bXVwL3NlcnZpY2VzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHlEQUF5RTtBQUFoRSx1SUFBQSxPQUFPLE9BQXdCIn0=
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SumUpClientError = void 0;
4
+ /**
5
+ * Typed error thrown by SumUpClient for all non-OK HTTP responses and
6
+ * network-level failures. Callers can inspect `.type` to decide how to
7
+ * handle (surface to user, retry, fall through, etc.).
8
+ */
9
+ class SumUpClientError extends Error {
10
+ constructor(message, type, statusCode) {
11
+ super(message);
12
+ this.name = "SumUpClientError";
13
+ this.type = type;
14
+ this.statusCode = statusCode;
15
+ }
16
+ }
17
+ exports.SumUpClientError = SumUpClientError;
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL3N1bXVwL3R5cGVzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTJHQTs7OztHQUlHO0FBQ0gsTUFBYSxnQkFBaUIsU0FBUSxLQUFLO0lBSXpDLFlBQ0UsT0FBZSxFQUNmLElBQW9CLEVBQ3BCLFVBQW1CO1FBRW5CLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsa0JBQWtCLENBQUM7UUFDL0IsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7SUFDL0IsQ0FBQztDQUNGO0FBZEQsNENBY0MifQ==
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # SumUp Payments for Medusa
2
+
3
+ A SumUp payment provider plugin for [Medusa](https://medusajs.com/) V2.
4
+ Inspired by https://github.com/VariableVic/mollie-payments-medusa and mostly implemented by Claude.
5
+
6
+ > [!WARNING]
7
+ > This plugin has not been tested on a live store. Please conduct thorough testing before using it in a production environment. I am not responsible for any missed or failed payments resulting from the use of this plugin. If you encounter any issues, please report them [here](https://github.com/meister-eder/medusa-payment-sumup/issues).
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Prerequisites](#prerequisites)
13
+ - [Installation](#installation)
14
+ - [Configuration](#configuration)
15
+ - [Configuration Options](#configuration-options)
16
+ - [Environment Variables](#environment-variables)
17
+ - [Usage](#usage)
18
+ - [Client-Side Integration](#client-side-integration)
19
+ - [License](#license)
20
+
21
+ ## Features
22
+
23
+ - **SumUp Checkout**: Creates a hosted SumUp checkout session that customers complete via the SumUp Card Widget or a custom card form.
24
+ - **Webhook Support**: Receives real-time checkout status updates via SumUp webhooks.
25
+ - **Refunds**: Supports full and partial refunds via the SumUp Transactions API.
26
+ - **No extra dependencies**: Uses the native Node.js `fetch` API — no additional HTTP client needed.
27
+
28
+ ## Prerequisites
29
+
30
+ - Medusa server v2.3.0 or later
31
+ - Node.js v20 or later
32
+ - A [SumUp](https://sumup.com/) merchant account with a valid API key and merchant code
33
+
34
+ > [!NOTE]
35
+ > You can find your API key and merchant code in your SumUp Dashboard under **Settings → Developer → API Keys**.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install medusa-payment-sumup
41
+ # or
42
+ yarn add medusa-payment-sumup
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ Add the provider to the `@medusajs/payment` module in your `medusa-config.ts` file:
48
+
49
+ ```ts
50
+ modules: [
51
+ // ... other modules
52
+ {
53
+ resolve: "@medusajs/payment",
54
+ options: {
55
+ providers: [
56
+ // ... other providers
57
+ {
58
+ resolve: "medusa-payment-sumup/providers/sumup",
59
+ id: "sumup",
60
+ options: {
61
+ apiKey: process.env.SUMUP_API_KEY,
62
+ merchantCode: process.env.SUMUP_MERCHANT_CODE,
63
+ medusaUrl: process.env.MEDUSA_BACKEND_URL,
64
+ redirectUrl: process.env.SUMUP_REDIRECT_URL,
65
+ },
66
+ },
67
+ ],
68
+ },
69
+ },
70
+ ]
71
+ ```
72
+
73
+ ### Configuration Options
74
+
75
+ | Option | Description | Required | Default |
76
+ |---|---|---|---|
77
+ | `apiKey` | Your SumUp API key (e.g. `sup_sk_...`) | ✅ | — |
78
+ | `merchantCode` | Your SumUp merchant code (e.g. `MH4H92C7`) | ✅ | — |
79
+ | `medusaUrl` | The public URL of your Medusa backend | ✅ | — |
80
+ | `redirectUrl` | The storefront URL to redirect the customer to after payment | ✅ | — |
81
+ | `debug` | Enable verbose debug logging | ❌ | `false` |
82
+
83
+ ### Environment Variables
84
+
85
+ Add the following to your `.env` file:
86
+
87
+ ```env
88
+ SUMUP_API_KEY=sup_sk_your_api_key
89
+ SUMUP_MERCHANT_CODE=MH4H92C7
90
+ MEDUSA_BACKEND_URL=https://your-medusa-server.com
91
+ SUMUP_REDIRECT_URL=https://your-store.com/checkout/payment
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ Once installed and configured, the SumUp payment method will be available in your Medusa Admin. To enable it:
97
+
98
+ 1. Log in to your Medusa Admin
99
+ 2. Go to **Settings → Regions**
100
+ 3. Add or edit a region and select **SumUp** from the payment providers dropdown
101
+
102
+ The provider ID for the SumUp checkout is:
103
+
104
+ ```
105
+ pp_sumup_sumup
106
+ ```
107
+
108
+ ## Client-Side Integration
109
+
110
+ SumUp checkouts are created server-side by Medusa. After a payment session is initiated, the session data will contain a SumUp checkout `id` that you pass to the [SumUp Card Widget](https://developer.sumup.com/online-payments/tools/card-widget/) on your storefront to render the payment form.
111
+
112
+ Basic flow:
113
+
114
+ 1. Customer reaches the payment step — Medusa creates a SumUp checkout via `initiatePayment`
115
+ 2. Your storefront receives the checkout `id` from the payment session data
116
+ 3. Render the SumUp Card Widget with the checkout ID
117
+ 4. SumUp processes the payment and calls the Medusa webhook (`/hooks/payment/sumup_sumup`)
118
+ 5. Medusa authorizes and captures the payment automatically
119
+
120
+ Example Card Widget integration:
121
+
122
+ ```html
123
+ <script src="https://gateway.sumup.com/gateway/ecom/card/v2/sdk.js"></script>
124
+ <div id="sumup-card"></div>
125
+
126
+ <script>
127
+ SumUpCard.mount({
128
+ checkoutId: "<checkout-id-from-session-data>",
129
+ onResponse: function (type, body) {
130
+ if (type === "success") {
131
+ // Redirect or poll for order completion
132
+ }
133
+ },
134
+ });
135
+ </script>
136
+ ```
137
+
138
+ For APMs (iDEAL, Bancontact, etc.), the `redirectUrl` option handles the post-payment redirect automatically.
139
+
140
+ ## License
141
+
142
+ MIT
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "medusa-payment-sumup",
3
+ "version": "0.1.0",
4
+ "description": "SumUp payment provider for Medusa v2",
5
+ "author": "Tim Eder <timeder92@gmail.com>",
6
+ "license": "MIT",
7
+ "files": [
8
+ ".medusa/server"
9
+ ],
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ "./providers/*": "./.medusa/server/src/providers/*/index.js",
13
+ "./*": "./.medusa/server/src/*.js"
14
+ },
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
18
+ "keywords": [
19
+ "medusa-plugin-integration",
20
+ "medusa-v2",
21
+ "medusa-plugin-payment",
22
+ "sumup",
23
+ "payment",
24
+ "payment-provider",
25
+ "ecommerce"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/meister-eder/medusa-payment-sumup.git"
30
+ },
31
+ "scripts": {
32
+ "build": "medusa plugin:build",
33
+ "dev": "medusa plugin:develop",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "prepublishOnly": "medusa plugin:build"
37
+ },
38
+ "dependencies": {},
39
+ "devDependencies": {
40
+ "@medusajs/cli": "^2.5.0",
41
+ "@medusajs/framework": "^2.5.0",
42
+ "@medusajs/medusa": "^2.5.0",
43
+ "@types/node": "^22.0.0",
44
+ "typescript": "^5.6.0",
45
+ "vitest": "^3.0.0"
46
+ },
47
+ "peerDependencies": {
48
+ "@medusajs/framework": "^2.5.0",
49
+ "@medusajs/medusa": "^2.5.0"
50
+ }
51
+ }