@paytree/medusa-payment-paytree 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.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +55 -0
- package/dist/modules/paytree/index.d.ts +36 -0
- package/dist/modules/paytree/index.js +13 -0
- package/dist/modules/paytree/migrations/Migration20260617000000.d.ts +5 -0
- package/dist/modules/paytree/migrations/Migration20260617000000.js +54 -0
- package/dist/modules/paytree/migrations/Migration20260618000000.d.ts +5 -0
- package/dist/modules/paytree/migrations/Migration20260618000000.js +19 -0
- package/dist/modules/paytree/models/paytree-event-log.d.ts +25 -0
- package/dist/modules/paytree/models/paytree-event-log.js +29 -0
- package/dist/modules/paytree/models/paytree-setting.d.ts +23 -0
- package/dist/modules/paytree/models/paytree-setting.js +27 -0
- package/dist/modules/paytree/service.d.ts +60 -0
- package/dist/modules/paytree/service.js +47 -0
- package/dist/modules/paytree-payment/index.d.ts +8 -0
- package/dist/modules/paytree-payment/index.js +16 -0
- package/dist/modules/paytree-payment/lib/amount.d.ts +23 -0
- package/dist/modules/paytree-payment/lib/amount.js +40 -0
- package/dist/modules/paytree-payment/lib/audit.d.ts +26 -0
- package/dist/modules/paytree-payment/lib/audit.js +51 -0
- package/dist/modules/paytree-payment/lib/client.d.ts +28 -0
- package/dist/modules/paytree-payment/lib/client.js +81 -0
- package/dist/modules/paytree-payment/lib/constants.d.ts +54 -0
- package/dist/modules/paytree-payment/lib/constants.js +55 -0
- package/dist/modules/paytree-payment/lib/logger.d.ts +27 -0
- package/dist/modules/paytree-payment/lib/logger.js +73 -0
- package/dist/modules/paytree-payment/lib/methods.d.ts +69 -0
- package/dist/modules/paytree-payment/lib/methods.js +68 -0
- package/dist/modules/paytree-payment/lib/redact.d.ts +16 -0
- package/dist/modules/paytree-payment/lib/redact.js +60 -0
- package/dist/modules/paytree-payment/lib/settings-reader.d.ts +24 -0
- package/dist/modules/paytree-payment/lib/settings-reader.js +74 -0
- package/dist/modules/paytree-payment/lib/status-map.d.ts +26 -0
- package/dist/modules/paytree-payment/lib/status-map.js +66 -0
- package/dist/modules/paytree-payment/lib/types.d.ts +144 -0
- package/dist/modules/paytree-payment/lib/types.js +2 -0
- package/dist/modules/paytree-payment/service.d.ts +36 -0
- package/dist/modules/paytree-payment/service.js +369 -0
- package/docs/api-reference.md +121 -0
- package/docs/architecture.md +97 -0
- package/docs/configuration.md +62 -0
- package/docs/getting-started.md +111 -0
- package/docs/storefront-integration.md +85 -0
- package/integration/README.md +46 -0
- package/integration/admin/routes/paytree/api.ts +16 -0
- package/integration/admin/routes/paytree/method-icons.tsx +106 -0
- package/integration/admin/routes/paytree/page.tsx +18 -0
- package/integration/admin/routes/paytree/payments/page.tsx +240 -0
- package/integration/admin/routes/paytree/settings/page.tsx +214 -0
- package/integration/api/admin/paytree/events/route.ts +35 -0
- package/integration/api/admin/paytree/settings/route.ts +86 -0
- package/integration/api/hooks/paytree/route.ts +195 -0
- package/integration/api/store/paytree/methods/route.ts +28 -0
- package/package.json +57 -0
- package/storefront/README.md +25 -0
- package/storefront/components/payment-button.tsx +361 -0
- package/storefront/components/payment.tsx +356 -0
- package/storefront/components/paytree-method-icon.tsx +106 -0
- package/storefront/lib/paytree.ts +23 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PAYTREE_METHOD_DEFS = void 0;
|
|
4
|
+
exports.resolveMethods = resolveMethods;
|
|
5
|
+
exports.normalizeMethods = normalizeMethods;
|
|
6
|
+
/**
|
|
7
|
+
* Canonical list of payment methods Paytree supports, with their default
|
|
8
|
+
* display labels. The admin can, per method:
|
|
9
|
+
* - toggle whether it is offered at checkout (`enabled`)
|
|
10
|
+
* - override the label shown to the customer (`label`)
|
|
11
|
+
*
|
|
12
|
+
* The set of methods is fixed by Paytree; only `enabled` and `label` are
|
|
13
|
+
* stored in the DB. Resolving merges the stored config over these defaults so
|
|
14
|
+
* that adding a method here automatically surfaces it in the admin UI.
|
|
15
|
+
*/
|
|
16
|
+
exports.PAYTREE_METHOD_DEFS = [
|
|
17
|
+
{ method: "card", label: "Credit / debit card" },
|
|
18
|
+
{ method: "paypal", label: "PayPal" },
|
|
19
|
+
{ method: "klarna", label: "Klarna" },
|
|
20
|
+
{ method: "ideal", label: "iDEAL" },
|
|
21
|
+
{ method: "bancontact", label: "Bancontact" },
|
|
22
|
+
{ method: "crypto", label: "Crypto" },
|
|
23
|
+
{ method: "wire", label: "Bank transfer" },
|
|
24
|
+
{ method: "trustly", label: "Trustly" },
|
|
25
|
+
{ method: "kakaopay", label: "KakaoPay" },
|
|
26
|
+
{ method: "p24", label: "Przelewy24" },
|
|
27
|
+
{ method: "blik", label: "BLIK" },
|
|
28
|
+
];
|
|
29
|
+
const VALID_METHODS = new Set(exports.PAYTREE_METHOD_DEFS.map((m) => m.method));
|
|
30
|
+
/**
|
|
31
|
+
* Merge stored per-method config over the canonical defaults. Unknown stored
|
|
32
|
+
* methods are ignored; missing ones fall back to disabled with the default
|
|
33
|
+
* label. `card` is enabled by default so a fresh install has one working
|
|
34
|
+
* method until the admin curates the list.
|
|
35
|
+
*/
|
|
36
|
+
function resolveMethods(stored) {
|
|
37
|
+
const byMethod = new Map((stored ?? [])
|
|
38
|
+
.filter((m) => m && VALID_METHODS.has(m.method))
|
|
39
|
+
.map((m) => [m.method, m]));
|
|
40
|
+
return exports.PAYTREE_METHOD_DEFS.map((def) => {
|
|
41
|
+
const s = byMethod.get(def.method);
|
|
42
|
+
const label = s?.label?.trim();
|
|
43
|
+
return {
|
|
44
|
+
method: def.method,
|
|
45
|
+
enabled: s ? Boolean(s.enabled) : def.method === "card",
|
|
46
|
+
label: label && label.length > 0 ? label : def.label,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalize an incoming admin payload into the minimal stored shape, dropping
|
|
52
|
+
* unknown methods and blank label overrides.
|
|
53
|
+
*/
|
|
54
|
+
function normalizeMethods(input) {
|
|
55
|
+
if (!Array.isArray(input)) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return input
|
|
59
|
+
.filter((m) => m && VALID_METHODS.has(m.method))
|
|
60
|
+
.map((m) => {
|
|
61
|
+
const label = typeof m.label === "string" ? m.label.trim() : "";
|
|
62
|
+
return {
|
|
63
|
+
method: m.method,
|
|
64
|
+
enabled: Boolean(m.enabled),
|
|
65
|
+
...(label ? { label } : {}),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction utilities.
|
|
3
|
+
*
|
|
4
|
+
* Request/response logging is opt-in via the admin toggles. When on, we log the
|
|
5
|
+
* full payload so it is actually useful for debugging — the only thing masked
|
|
6
|
+
* is the API key (and equivalent credentials), which lives in the Authorization
|
|
7
|
+
* header. Customer details and addresses are shown as-is.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Deep-clone a value with credential fields masked. Safe to pass any JSON-ish
|
|
11
|
+
* object, including arrays and nested structures. Non-object values are
|
|
12
|
+
* returned as-is.
|
|
13
|
+
*/
|
|
14
|
+
export declare function redact(input: unknown): unknown;
|
|
15
|
+
/** Mask the API key in the Authorization header for safe logging. */
|
|
16
|
+
export declare function redactHeaders(headers: Record<string, string>): Record<string, string>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redaction utilities.
|
|
4
|
+
*
|
|
5
|
+
* Request/response logging is opt-in via the admin toggles. When on, we log the
|
|
6
|
+
* full payload so it is actually useful for debugging — the only thing masked
|
|
7
|
+
* is the API key (and equivalent credentials), which lives in the Authorization
|
|
8
|
+
* header. Customer details and addresses are shown as-is.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.redact = redact;
|
|
12
|
+
exports.redactHeaders = redactHeaders;
|
|
13
|
+
const MASK = "[redacted]";
|
|
14
|
+
/**
|
|
15
|
+
* Credential keys masked wherever they appear, at any depth. Deliberately
|
|
16
|
+
* limited to secrets — not PII — so logs stay debuggable. The API key normally
|
|
17
|
+
* only appears in the Authorization header (see redactHeaders).
|
|
18
|
+
*/
|
|
19
|
+
const FULLY_MASKED_KEYS = new Set([
|
|
20
|
+
"authorization",
|
|
21
|
+
"apikey",
|
|
22
|
+
"api_key",
|
|
23
|
+
"token",
|
|
24
|
+
"secret",
|
|
25
|
+
"password",
|
|
26
|
+
]);
|
|
27
|
+
/**
|
|
28
|
+
* Deep-clone a value with credential fields masked. Safe to pass any JSON-ish
|
|
29
|
+
* object, including arrays and nested structures. Non-object values are
|
|
30
|
+
* returned as-is.
|
|
31
|
+
*/
|
|
32
|
+
function redact(input) {
|
|
33
|
+
if (input === null || input === undefined) {
|
|
34
|
+
return input;
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(input)) {
|
|
37
|
+
return input.map((item) => redact(item));
|
|
38
|
+
}
|
|
39
|
+
if (typeof input === "object") {
|
|
40
|
+
const out = {};
|
|
41
|
+
for (const [key, value] of Object.entries(input)) {
|
|
42
|
+
if (FULLY_MASKED_KEYS.has(key.toLowerCase())) {
|
|
43
|
+
out[key] = MASK;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
out[key] = redact(value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
return input;
|
|
52
|
+
}
|
|
53
|
+
/** Mask the API key in the Authorization header for safe logging. */
|
|
54
|
+
function redactHeaders(headers) {
|
|
55
|
+
const out = {};
|
|
56
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
57
|
+
out[key] = FULLY_MASKED_KEYS.has(key.toLowerCase()) ? MASK : value;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PaytreeEffectiveSettings, PaytreeProviderOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The payment provider runs inside the Payment module's isolated container, so
|
|
4
|
+
* it cannot resolve the `paytree` module's service to read settings. Medusa's
|
|
5
|
+
* module isolation only restricts service resolution, not physical database
|
|
6
|
+
* access, so we read the shared `paytree_setting` table directly through the
|
|
7
|
+
* Postgres connection that Medusa registers in the container.
|
|
8
|
+
*
|
|
9
|
+
* The `paytree` module owns the schema and migration for this table; here we
|
|
10
|
+
* only read from it, with a short TTL cache so we don't hit the DB on every
|
|
11
|
+
* provider call.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SettingsReader {
|
|
14
|
+
private readonly container_;
|
|
15
|
+
private readonly options_;
|
|
16
|
+
private cache_;
|
|
17
|
+
constructor(container: any, options: PaytreeProviderOptions);
|
|
18
|
+
private fallback;
|
|
19
|
+
/** Resolve the knex connection from the container, tolerating the proxy quirk. */
|
|
20
|
+
private getConnection;
|
|
21
|
+
/** Force the next read to hit the database. */
|
|
22
|
+
invalidate(): void;
|
|
23
|
+
get(): Promise<PaytreeEffectiveSettings>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SettingsReader = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
/**
|
|
7
|
+
* The payment provider runs inside the Payment module's isolated container, so
|
|
8
|
+
* it cannot resolve the `paytree` module's service to read settings. Medusa's
|
|
9
|
+
* module isolation only restricts service resolution, not physical database
|
|
10
|
+
* access, so we read the shared `paytree_setting` table directly through the
|
|
11
|
+
* Postgres connection that Medusa registers in the container.
|
|
12
|
+
*
|
|
13
|
+
* The `paytree` module owns the schema and migration for this table; here we
|
|
14
|
+
* only read from it, with a short TTL cache so we don't hit the DB on every
|
|
15
|
+
* provider call.
|
|
16
|
+
*/
|
|
17
|
+
class SettingsReader {
|
|
18
|
+
constructor(container, options) {
|
|
19
|
+
this.cache_ = null;
|
|
20
|
+
this.container_ = container;
|
|
21
|
+
this.options_ = options;
|
|
22
|
+
}
|
|
23
|
+
fallback() {
|
|
24
|
+
return {
|
|
25
|
+
baseUrl: this.options_.baseUrl || constants_1.DEFAULT_BASE_URL,
|
|
26
|
+
logRequest: false,
|
|
27
|
+
logResponse: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Resolve the knex connection from the container, tolerating the proxy quirk. */
|
|
31
|
+
getConnection() {
|
|
32
|
+
try {
|
|
33
|
+
return this.container_.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// The MedusaContainer proxy injected into providers can shadow `resolve`
|
|
37
|
+
// for some keys; fall back to direct property access.
|
|
38
|
+
return this.container_?.[utils_1.ContainerRegistrationKeys.PG_CONNECTION] ?? null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Force the next read to hit the database. */
|
|
42
|
+
invalidate() {
|
|
43
|
+
this.cache_ = null;
|
|
44
|
+
}
|
|
45
|
+
async get() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
if (this.cache_ && this.cache_.expiresAt > now) {
|
|
48
|
+
return this.cache_.value;
|
|
49
|
+
}
|
|
50
|
+
const knex = this.getConnection();
|
|
51
|
+
let value = this.fallback();
|
|
52
|
+
if (knex) {
|
|
53
|
+
try {
|
|
54
|
+
const row = await knex(constants_1.SETTINGS_TABLE)
|
|
55
|
+
.where({ id: constants_1.SETTINGS_SINGLETON_ID })
|
|
56
|
+
.first();
|
|
57
|
+
if (row) {
|
|
58
|
+
value = {
|
|
59
|
+
baseUrl: row.base_url || value.baseUrl,
|
|
60
|
+
logRequest: Boolean(row.log_request),
|
|
61
|
+
logResponse: Boolean(row.log_response),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Table may not exist yet (migrations not run) — use the fallback.
|
|
67
|
+
value = this.fallback();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.cache_ = { value, expiresAt: now + constants_1.SETTINGS_CACHE_TTL_MS };
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.SettingsReader = SettingsReader;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PaymentActions, PaymentSessionStatus } from "@medusajs/framework/utils";
|
|
2
|
+
import { PaytreeStatusValue } from "./constants";
|
|
3
|
+
/**
|
|
4
|
+
* Map a Paytree transaction status to a Medusa PaymentSessionStatus.
|
|
5
|
+
*
|
|
6
|
+
* Paytree auto-captures on the hosted page, so a `success` status means the
|
|
7
|
+
* funds are captured. When `autoCapture` is false (reserved for a future
|
|
8
|
+
* auth/capture flow), `success` is treated as `authorized` instead.
|
|
9
|
+
*/
|
|
10
|
+
export declare function toSessionStatus(status: PaytreeStatusValue, autoCapture?: boolean): PaymentSessionStatus;
|
|
11
|
+
export type CallbackOutcome = {
|
|
12
|
+
kind: "action";
|
|
13
|
+
action: PaymentActions;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "refund";
|
|
16
|
+
} | {
|
|
17
|
+
kind: "ignore";
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Decide what the callback route should do for a given Paytree status.
|
|
21
|
+
*
|
|
22
|
+
* Medusa's webhook pipeline only acts on `authorized` and `captured` actions,
|
|
23
|
+
* so refunds cannot be expressed as a webhook action. They are routed to the
|
|
24
|
+
* refund workflow instead (kind: "refund").
|
|
25
|
+
*/
|
|
26
|
+
export declare function callbackOutcome(status: PaytreeStatusValue): CallbackOutcome;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toSessionStatus = toSessionStatus;
|
|
4
|
+
exports.callbackOutcome = callbackOutcome;
|
|
5
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
6
|
+
const constants_1 = require("./constants");
|
|
7
|
+
/**
|
|
8
|
+
* Map a Paytree transaction status to a Medusa PaymentSessionStatus.
|
|
9
|
+
*
|
|
10
|
+
* Paytree auto-captures on the hosted page, so a `success` status means the
|
|
11
|
+
* funds are captured. When `autoCapture` is false (reserved for a future
|
|
12
|
+
* auth/capture flow), `success` is treated as `authorized` instead.
|
|
13
|
+
*/
|
|
14
|
+
function toSessionStatus(status, autoCapture = true) {
|
|
15
|
+
switch (status) {
|
|
16
|
+
case constants_1.PaytreeStatus.PENDING:
|
|
17
|
+
case constants_1.PaytreeStatus.PARTIAL:
|
|
18
|
+
return utils_1.PaymentSessionStatus.PENDING;
|
|
19
|
+
case constants_1.PaytreeStatus.AUTHORIZED:
|
|
20
|
+
return utils_1.PaymentSessionStatus.AUTHORIZED;
|
|
21
|
+
case constants_1.PaytreeStatus.SUCCESS:
|
|
22
|
+
return autoCapture
|
|
23
|
+
? utils_1.PaymentSessionStatus.CAPTURED
|
|
24
|
+
: utils_1.PaymentSessionStatus.AUTHORIZED;
|
|
25
|
+
case constants_1.PaytreeStatus.FAILED:
|
|
26
|
+
return utils_1.PaymentSessionStatus.ERROR;
|
|
27
|
+
case constants_1.PaytreeStatus.CANCELED:
|
|
28
|
+
case constants_1.PaytreeStatus.EXPIRED:
|
|
29
|
+
return utils_1.PaymentSessionStatus.CANCELED;
|
|
30
|
+
// Refunds and disputes leave the session captured; refunds are tracked
|
|
31
|
+
// as separate refund records, disputes are handled out of band.
|
|
32
|
+
case constants_1.PaytreeStatus.REFUNDED:
|
|
33
|
+
case constants_1.PaytreeStatus.PARTIAL_REFUNDED:
|
|
34
|
+
case constants_1.PaytreeStatus.DISPUTED:
|
|
35
|
+
case constants_1.PaytreeStatus.DISPUTED_WON:
|
|
36
|
+
case constants_1.PaytreeStatus.DISPUTED_LOST:
|
|
37
|
+
case constants_1.PaytreeStatus.DISPUTED_CLOSE:
|
|
38
|
+
return utils_1.PaymentSessionStatus.CAPTURED;
|
|
39
|
+
default:
|
|
40
|
+
return utils_1.PaymentSessionStatus.PENDING;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decide what the callback route should do for a given Paytree status.
|
|
45
|
+
*
|
|
46
|
+
* Medusa's webhook pipeline only acts on `authorized` and `captured` actions,
|
|
47
|
+
* so refunds cannot be expressed as a webhook action. They are routed to the
|
|
48
|
+
* refund workflow instead (kind: "refund").
|
|
49
|
+
*/
|
|
50
|
+
function callbackOutcome(status) {
|
|
51
|
+
switch (status) {
|
|
52
|
+
case constants_1.PaytreeStatus.SUCCESS:
|
|
53
|
+
return { kind: "action", action: utils_1.PaymentActions.SUCCESSFUL };
|
|
54
|
+
case constants_1.PaytreeStatus.AUTHORIZED:
|
|
55
|
+
return { kind: "action", action: utils_1.PaymentActions.AUTHORIZED };
|
|
56
|
+
case constants_1.PaytreeStatus.FAILED:
|
|
57
|
+
case constants_1.PaytreeStatus.EXPIRED:
|
|
58
|
+
case constants_1.PaytreeStatus.CANCELED:
|
|
59
|
+
return { kind: "action", action: utils_1.PaymentActions.FAILED };
|
|
60
|
+
case constants_1.PaytreeStatus.REFUNDED:
|
|
61
|
+
case constants_1.PaytreeStatus.PARTIAL_REFUNDED:
|
|
62
|
+
return { kind: "refund" };
|
|
63
|
+
default:
|
|
64
|
+
return { kind: "ignore" };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { PaytreeStatusValue } from "./constants";
|
|
2
|
+
/**
|
|
3
|
+
* Options passed to the provider from medusa-config.ts.
|
|
4
|
+
* The API key is ALWAYS sourced from an environment variable and never
|
|
5
|
+
* stored in the database or shown in the admin UI.
|
|
6
|
+
*/
|
|
7
|
+
export interface PaytreeProviderOptions {
|
|
8
|
+
/** Paytree API key. Sent as `Authorization: Token <apiKey>`. From env. */
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/**
|
|
11
|
+
* Public URL of this Medusa backend, used to build the notification URL
|
|
12
|
+
* Paytree calls back. e.g. https://api.mystore.com
|
|
13
|
+
*/
|
|
14
|
+
backendUrl: string;
|
|
15
|
+
/**
|
|
16
|
+
* Fallback base URL if no settings row exists yet in the DB.
|
|
17
|
+
* The admin-editable value in the DB takes precedence at runtime.
|
|
18
|
+
*/
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Default payment method requested from Paytree when the storefront
|
|
22
|
+
* doesn't specify one. Defaults to "card".
|
|
23
|
+
*/
|
|
24
|
+
defaultMethod?: PaytreeMethod;
|
|
25
|
+
}
|
|
26
|
+
/** Effective settings resolved at runtime (DB row, falling back to options). */
|
|
27
|
+
export interface PaytreeEffectiveSettings {
|
|
28
|
+
baseUrl: string;
|
|
29
|
+
logRequest: boolean;
|
|
30
|
+
logResponse: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type PaytreeMethod = "card" | "crypto" | "wire" | "paypal" | "klarna" | "ideal" | "bancontact" | "trustly" | "kakaopay" | "p24" | "blik";
|
|
33
|
+
/** Data the storefront must supply when creating the payment session. */
|
|
34
|
+
export interface PaytreeSessionData {
|
|
35
|
+
/** Stable correlation key. We default this to the Medusa payment session id. */
|
|
36
|
+
session_id?: string;
|
|
37
|
+
customer?: {
|
|
38
|
+
first_name?: string;
|
|
39
|
+
last_name?: string;
|
|
40
|
+
email?: string;
|
|
41
|
+
phone?: string;
|
|
42
|
+
};
|
|
43
|
+
address?: {
|
|
44
|
+
street?: string;
|
|
45
|
+
city?: string;
|
|
46
|
+
state?: string;
|
|
47
|
+
zip?: string;
|
|
48
|
+
/** ISO 3166-1 alpha-2 country code. Required by Paytree. */
|
|
49
|
+
country?: string;
|
|
50
|
+
};
|
|
51
|
+
/** End-customer IP and UA, required by Paytree for fraud screening. */
|
|
52
|
+
session?: {
|
|
53
|
+
ip_address?: string;
|
|
54
|
+
user_agent?: string;
|
|
55
|
+
};
|
|
56
|
+
return_url?: string;
|
|
57
|
+
cancel_url?: string;
|
|
58
|
+
method?: PaytreeMethod;
|
|
59
|
+
order_items?: PaytreeOrderItem[];
|
|
60
|
+
/** Set after we create the intent. */
|
|
61
|
+
paytree_id?: string;
|
|
62
|
+
payment_link?: string;
|
|
63
|
+
transaction_ref?: string;
|
|
64
|
+
status?: PaytreeStatusValue;
|
|
65
|
+
}
|
|
66
|
+
export interface PaytreeOrderItem {
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
quantity: number;
|
|
70
|
+
unit_price: string;
|
|
71
|
+
sku: string;
|
|
72
|
+
}
|
|
73
|
+
/** Body sent to POST /v1/transaction/payment_intent/. */
|
|
74
|
+
export interface PaytreeCreateIntentBody {
|
|
75
|
+
transaction_ref: string;
|
|
76
|
+
client_ref: string;
|
|
77
|
+
amount: string;
|
|
78
|
+
amount_currency: string;
|
|
79
|
+
method: PaytreeMethod;
|
|
80
|
+
customer: {
|
|
81
|
+
first_name: string;
|
|
82
|
+
last_name: string;
|
|
83
|
+
email: string;
|
|
84
|
+
phone?: string;
|
|
85
|
+
};
|
|
86
|
+
address: {
|
|
87
|
+
street?: string;
|
|
88
|
+
city?: string;
|
|
89
|
+
state?: string;
|
|
90
|
+
zip?: string;
|
|
91
|
+
country: string;
|
|
92
|
+
};
|
|
93
|
+
session: {
|
|
94
|
+
ip_address: string;
|
|
95
|
+
user_agent: string;
|
|
96
|
+
};
|
|
97
|
+
callback: {
|
|
98
|
+
notification_url: string;
|
|
99
|
+
return_url: string;
|
|
100
|
+
cancel_url?: string;
|
|
101
|
+
};
|
|
102
|
+
metadata?: Record<string, unknown>;
|
|
103
|
+
order_items?: PaytreeOrderItem[];
|
|
104
|
+
}
|
|
105
|
+
/** 201 response from creating a payment intent. */
|
|
106
|
+
export interface PaytreeCreateIntentResponse {
|
|
107
|
+
id: number | string;
|
|
108
|
+
client_ref: string;
|
|
109
|
+
payment_link: string;
|
|
110
|
+
}
|
|
111
|
+
/** A nested payment object within a payment intent. */
|
|
112
|
+
export interface PaytreePayment {
|
|
113
|
+
id: string;
|
|
114
|
+
amount: string;
|
|
115
|
+
currency: string;
|
|
116
|
+
base_amount?: string;
|
|
117
|
+
base_currency?: string;
|
|
118
|
+
fee?: string;
|
|
119
|
+
refunded?: string;
|
|
120
|
+
status: PaytreeStatusValue;
|
|
121
|
+
refund?: PaytreeRefund[];
|
|
122
|
+
created: string;
|
|
123
|
+
}
|
|
124
|
+
export interface PaytreeRefund {
|
|
125
|
+
id: string;
|
|
126
|
+
amount: string;
|
|
127
|
+
currency: string;
|
|
128
|
+
payment: string;
|
|
129
|
+
gateway_id: string;
|
|
130
|
+
created: string;
|
|
131
|
+
}
|
|
132
|
+
/** GET /v1/transaction/payment_intent/{id}/ response. */
|
|
133
|
+
export interface PaytreePaymentIntent {
|
|
134
|
+
id: string;
|
|
135
|
+
transaction_ref: string;
|
|
136
|
+
client_ref: string;
|
|
137
|
+
amount: string;
|
|
138
|
+
currency: string;
|
|
139
|
+
method: PaytreeMethod;
|
|
140
|
+
payment_link: string;
|
|
141
|
+
payment: PaytreePayment[];
|
|
142
|
+
metadata?: Record<string, unknown>;
|
|
143
|
+
created: string;
|
|
144
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AbstractPaymentProvider } from "@medusajs/framework/utils";
|
|
2
|
+
import type { AuthorizePaymentInput, AuthorizePaymentOutput, CancelPaymentInput, CancelPaymentOutput, CapturePaymentInput, CapturePaymentOutput, DeletePaymentInput, DeletePaymentOutput, GetPaymentStatusInput, GetPaymentStatusOutput, InitiatePaymentInput, InitiatePaymentOutput, ProviderWebhookPayload, RefundPaymentInput, RefundPaymentOutput, RetrievePaymentInput, RetrievePaymentOutput, UpdatePaymentInput, UpdatePaymentOutput, WebhookActionResult } from "@medusajs/framework/types";
|
|
3
|
+
import { SettingsReader } from "./lib/settings-reader";
|
|
4
|
+
import { AuditWriter } from "./lib/audit";
|
|
5
|
+
import { PaytreeLogger } from "./lib/logger";
|
|
6
|
+
import { PaytreeProviderOptions } from "./lib/types";
|
|
7
|
+
type Injected = Record<string, unknown>;
|
|
8
|
+
export declare class PaytreePaymentProviderService extends AbstractPaymentProvider<PaytreeProviderOptions> {
|
|
9
|
+
static identifier: string;
|
|
10
|
+
protected readonly options_: PaytreeProviderOptions;
|
|
11
|
+
protected readonly container_: any;
|
|
12
|
+
protected readonly logger_: PaytreeLogger;
|
|
13
|
+
protected readonly settings_: SettingsReader;
|
|
14
|
+
protected readonly audit_: AuditWriter;
|
|
15
|
+
static validateOptions(options: PaytreeProviderOptions): void;
|
|
16
|
+
constructor(container: Injected, options: PaytreeProviderOptions);
|
|
17
|
+
/** Build a client using the current effective settings (live base URL + toggles). */
|
|
18
|
+
private client;
|
|
19
|
+
/** Notification URL Paytree will call back, with its substitution tags. */
|
|
20
|
+
private notificationUrl;
|
|
21
|
+
initiatePayment(input: InitiatePaymentInput): Promise<InitiatePaymentOutput>;
|
|
22
|
+
/** Read the live intent from Paytree by transaction_ref. */
|
|
23
|
+
private readIntent;
|
|
24
|
+
/** Roll up the latest status across the intent's payment objects. */
|
|
25
|
+
private latestStatus;
|
|
26
|
+
getPaymentStatus(input: GetPaymentStatusInput): Promise<GetPaymentStatusOutput>;
|
|
27
|
+
retrievePayment(input: RetrievePaymentInput): Promise<RetrievePaymentOutput>;
|
|
28
|
+
authorizePayment(input: AuthorizePaymentInput): Promise<AuthorizePaymentOutput>;
|
|
29
|
+
capturePayment(input: CapturePaymentInput): Promise<CapturePaymentOutput>;
|
|
30
|
+
cancelPayment(input: CancelPaymentInput): Promise<CancelPaymentOutput>;
|
|
31
|
+
deletePayment(input: DeletePaymentInput): Promise<DeletePaymentOutput>;
|
|
32
|
+
updatePayment(input: UpdatePaymentInput): Promise<UpdatePaymentOutput>;
|
|
33
|
+
refundPayment(input: RefundPaymentInput): Promise<RefundPaymentOutput>;
|
|
34
|
+
getWebhookActionAndData(payload: ProviderWebhookPayload["payload"]): Promise<WebhookActionResult>;
|
|
35
|
+
}
|
|
36
|
+
export default PaytreePaymentProviderService;
|