@paykit-sdk/medusajs 1.0.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/README.md +66 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +311 -0
- package/dist/index.mjs +306 -0
- package/dist/providers/paykit-provider.d.mts +61 -0
- package/dist/providers/paykit-provider.d.ts +61 -0
- package/dist/providers/paykit-provider.js +303 -0
- package/dist/providers/paykit-provider.mjs +301 -0
- package/dist/utils/mapper.d.mts +6 -0
- package/dist/utils/mapper.d.ts +6 -0
- package/dist/utils/mapper.js +26 -0
- package/dist/utils/mapper.mjs +24 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @paykit-sdk/medusajs
|
|
2
|
+
|
|
3
|
+
Universal payment provider adapter for Medusa v2+ using PayKit. Compatible with any PayKit provider.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @paykit-sdk/medusajs @paykit-sdk/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Install your provider
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @paykit-sdk/stripe
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Configure in `medusa-config.ts`:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { defineConfig } from '@medusajs/framework/utils';
|
|
23
|
+
import { createStripe } from '@paykit-sdk/stripe';
|
|
24
|
+
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
modules: [
|
|
27
|
+
{
|
|
28
|
+
resolve: '@paykit-sdk/medusa-adapter',
|
|
29
|
+
options: {
|
|
30
|
+
provider: createStripe({ apiKey: process.env.STRIPE_API_KEY }),
|
|
31
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
32
|
+
debug: process.env.NODE_ENV === 'development',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Environment Variables
|
|
40
|
+
|
|
41
|
+
```env
|
|
42
|
+
STRIPE_API_KEY=sk_test_...
|
|
43
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Webhook Setup
|
|
47
|
+
|
|
48
|
+
Configure your payment provider to send webhooks to:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
https://your-store.com/hooks/payment/pp_paykit
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Select payment-related events based on the PayKit provider documentation.
|
|
55
|
+
|
|
56
|
+
## Configuration Options
|
|
57
|
+
|
|
58
|
+
| Option | Type | Required | Description |
|
|
59
|
+
| ------------- | -------------- | -------- | ---------------------------- |
|
|
60
|
+
| provider | PayKitProvider | Yes | PayKit provider instance |
|
|
61
|
+
| webhookSecret | string | Yes | Webhook secret from provider |
|
|
62
|
+
| debug | boolean | No | Enable debug logging |
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
ISC
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as _medusajs_types from '@medusajs/types';
|
|
2
|
+
export { PaykitMedusaJSAdapter, PaykitMedusaJSAdapterOptions } from './providers/paykit-provider.mjs';
|
|
3
|
+
import '@medusajs/framework/types';
|
|
4
|
+
import '@medusajs/framework/utils';
|
|
5
|
+
import '@paykit-sdk/core';
|
|
6
|
+
import 'zod';
|
|
7
|
+
|
|
8
|
+
declare const _default: _medusajs_types.ModuleProviderExports;
|
|
9
|
+
|
|
10
|
+
export { _default as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as _medusajs_types from '@medusajs/types';
|
|
2
|
+
export { PaykitMedusaJSAdapter, PaykitMedusaJSAdapterOptions } from './providers/paykit-provider.js';
|
|
3
|
+
import '@medusajs/framework/types';
|
|
4
|
+
import '@medusajs/framework/utils';
|
|
5
|
+
import '@paykit-sdk/core';
|
|
6
|
+
import 'zod';
|
|
7
|
+
|
|
8
|
+
declare const _default: _medusajs_types.ModuleProviderExports;
|
|
9
|
+
|
|
10
|
+
export { _default as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var utils = require('@medusajs/framework/utils');
|
|
6
|
+
var core = require('@paykit-sdk/core');
|
|
7
|
+
var zod = require('zod');
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
var medusaStatus$InboundSchema = (status) => {
|
|
11
|
+
switch (status) {
|
|
12
|
+
case "pending":
|
|
13
|
+
case "processing":
|
|
14
|
+
return utils.PaymentSessionStatus.PENDING;
|
|
15
|
+
case "requires_action":
|
|
16
|
+
return utils.PaymentSessionStatus.REQUIRES_MORE;
|
|
17
|
+
case "requires_capture":
|
|
18
|
+
return utils.PaymentSessionStatus.AUTHORIZED;
|
|
19
|
+
case "succeeded":
|
|
20
|
+
return utils.PaymentSessionStatus.CAPTURED;
|
|
21
|
+
case "canceled":
|
|
22
|
+
return utils.PaymentSessionStatus.CANCELED;
|
|
23
|
+
case "failed":
|
|
24
|
+
return utils.PaymentSessionStatus.ERROR;
|
|
25
|
+
default:
|
|
26
|
+
return utils.PaymentSessionStatus.PENDING;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/providers/paykit-provider.ts
|
|
31
|
+
var optionsSchema = zod.z.object({
|
|
32
|
+
/**
|
|
33
|
+
* The underlying PayKit provider instance (Stripe, PayPal, etc.)
|
|
34
|
+
* This is required and must be a valid PayKit provider instance
|
|
35
|
+
*/
|
|
36
|
+
provider: core.providerSchema,
|
|
37
|
+
/**
|
|
38
|
+
* The webhook secret for the provider
|
|
39
|
+
* This is required and must be a valid webhook secret
|
|
40
|
+
*/
|
|
41
|
+
webhookSecret: zod.z.string(),
|
|
42
|
+
/**
|
|
43
|
+
* Whether to enable debug mode
|
|
44
|
+
* If enabled, the adapter will log debug information to the console
|
|
45
|
+
*/
|
|
46
|
+
debug: zod.z.boolean().optional()
|
|
47
|
+
});
|
|
48
|
+
var PaykitMedusaJSAdapter = class extends utils.AbstractPaymentProvider {
|
|
49
|
+
/**
|
|
50
|
+
* The unique identifier for this payment provider
|
|
51
|
+
* Will be stored as `pp_paykit_{id}` in Medusa
|
|
52
|
+
*/
|
|
53
|
+
static identifier = "paykit";
|
|
54
|
+
paykit;
|
|
55
|
+
provider;
|
|
56
|
+
options;
|
|
57
|
+
static validateOptions(options) {
|
|
58
|
+
const { error } = optionsSchema.safeParse(options);
|
|
59
|
+
if (error) {
|
|
60
|
+
throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, error.message);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Constructor receives Medusa's container and provider options
|
|
66
|
+
*
|
|
67
|
+
* @param cradle - Medusa's dependency injection container
|
|
68
|
+
* @param options - PayKit provider configuration
|
|
69
|
+
*/
|
|
70
|
+
constructor(cradle, options) {
|
|
71
|
+
super(cradle, options);
|
|
72
|
+
this.options = options;
|
|
73
|
+
this.provider = options.provider;
|
|
74
|
+
this.paykit = new core.PayKit(this.provider);
|
|
75
|
+
if (this.options.debug) {
|
|
76
|
+
console.info(`[PayKit] Initialized with provider: ${this.provider.providerName}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
initiatePayment = async ({
|
|
80
|
+
context,
|
|
81
|
+
amount,
|
|
82
|
+
currency_code,
|
|
83
|
+
data
|
|
84
|
+
}) => {
|
|
85
|
+
if (this.options.debug) {
|
|
86
|
+
console.info("[PayKit] Initiating payment", { context, amount, currency_code, data });
|
|
87
|
+
}
|
|
88
|
+
const intent = {
|
|
89
|
+
status: "pending",
|
|
90
|
+
amount: Number(amount),
|
|
91
|
+
currency: currency_code,
|
|
92
|
+
metadata: { ...data?.metadata ?? {}, session_id: data?.session_id ?? "" },
|
|
93
|
+
provider_metadata: data?.provider_metadata ?? void 0
|
|
94
|
+
};
|
|
95
|
+
let customer;
|
|
96
|
+
if (context?.account_holder?.data?.id) {
|
|
97
|
+
customer = context.account_holder.data.id;
|
|
98
|
+
} else if (data?.email) {
|
|
99
|
+
customer = { email: data.email };
|
|
100
|
+
}
|
|
101
|
+
if (!customer) {
|
|
102
|
+
throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Required: customer ID (account_holder) or email (data)");
|
|
103
|
+
}
|
|
104
|
+
if (typeof customer === "object" && "email" in customer) {
|
|
105
|
+
await core.tryCatchAsync(
|
|
106
|
+
this.paykit.customers.create({
|
|
107
|
+
email: customer.email,
|
|
108
|
+
phone: data?.phone ?? "",
|
|
109
|
+
name: data?.name ? data.name : customer.email.split("@")[0],
|
|
110
|
+
metadata: { PAYKIT_METADATA_KEY: JSON.stringify({ source: "medusa" }) }
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
customer = customer;
|
|
115
|
+
}
|
|
116
|
+
intent.customer = customer;
|
|
117
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(
|
|
118
|
+
this.paykit.payments.create(intent)
|
|
119
|
+
);
|
|
120
|
+
if (paymentIntentError)
|
|
121
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
122
|
+
return { id: paymentIntentResult.id, status: medusaStatus$InboundSchema(paymentIntentResult.status) };
|
|
123
|
+
};
|
|
124
|
+
capturePayment = async (input) => {
|
|
125
|
+
if (this.options.debug) {
|
|
126
|
+
console.info("[PayKit] Capturing payment", input);
|
|
127
|
+
}
|
|
128
|
+
const { id, amount } = core.validateRequiredKeys(
|
|
129
|
+
["id", "amount"],
|
|
130
|
+
input?.data ?? {},
|
|
131
|
+
"Missing required payment ID"
|
|
132
|
+
);
|
|
133
|
+
if (!id) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
134
|
+
if (!amount) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment amount");
|
|
135
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(
|
|
136
|
+
this.paykit.payments.capture(id, { amount: Number(amount) })
|
|
137
|
+
);
|
|
138
|
+
if (paymentIntentError)
|
|
139
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
140
|
+
return { data: paymentIntentResult };
|
|
141
|
+
};
|
|
142
|
+
authorizePayment = async (input) => {
|
|
143
|
+
if (this.options.debug) {
|
|
144
|
+
console.info("[PayKit] Authorizing payment", input);
|
|
145
|
+
}
|
|
146
|
+
return this.getPaymentStatus(input);
|
|
147
|
+
};
|
|
148
|
+
cancelPayment = async (input) => {
|
|
149
|
+
if (this.options.debug) {
|
|
150
|
+
console.info("[PayKit] Canceling payment", input);
|
|
151
|
+
}
|
|
152
|
+
const { id } = core.validateRequiredKeys(
|
|
153
|
+
["id"],
|
|
154
|
+
input?.data ?? {},
|
|
155
|
+
"Missing required payment ID"
|
|
156
|
+
);
|
|
157
|
+
if (!id) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
158
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(this.paykit.payments.cancel(id));
|
|
159
|
+
if (paymentIntentError)
|
|
160
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
161
|
+
return { data: paymentIntentResult };
|
|
162
|
+
};
|
|
163
|
+
deletePayment = async (input) => {
|
|
164
|
+
return this.cancelPayment(input);
|
|
165
|
+
};
|
|
166
|
+
getPaymentStatus = async (input) => {
|
|
167
|
+
const { id } = core.validateRequiredKeys(
|
|
168
|
+
["id"],
|
|
169
|
+
input?.data ?? {},
|
|
170
|
+
"Missing required payment ID"
|
|
171
|
+
);
|
|
172
|
+
if (!id) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
173
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(this.paykit.payments.retrieve(id));
|
|
174
|
+
if (paymentIntentError)
|
|
175
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
176
|
+
if (!paymentIntentResult) throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, "Payment not found");
|
|
177
|
+
return {
|
|
178
|
+
status: medusaStatus$InboundSchema(paymentIntentResult.status),
|
|
179
|
+
data: paymentIntentResult
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
refundPayment = async (input) => {
|
|
183
|
+
if (this.options.debug) {
|
|
184
|
+
console.info("[PayKit] Refunding payment", input);
|
|
185
|
+
}
|
|
186
|
+
const { id: paymentId } = core.validateRequiredKeys(
|
|
187
|
+
["id"],
|
|
188
|
+
input?.data ?? {},
|
|
189
|
+
"Missing required payment ID"
|
|
190
|
+
);
|
|
191
|
+
if (!paymentId) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
192
|
+
const [refundResult, refundError] = await core.tryCatchAsync(
|
|
193
|
+
this.paykit.refunds.create({
|
|
194
|
+
payment_id: paymentId,
|
|
195
|
+
amount: Number(input.amount),
|
|
196
|
+
reason: null,
|
|
197
|
+
metadata: input.data?.metadata ? input.data.metadata : null,
|
|
198
|
+
provider_metadata: input.data?.provider_metadata ? input.data.provider_metadata : void 0
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
if (refundError) throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, refundError.message);
|
|
202
|
+
return { data: refundResult };
|
|
203
|
+
};
|
|
204
|
+
retrievePayment = async (input) => {
|
|
205
|
+
if (this.options.debug) {
|
|
206
|
+
console.info("[PayKit] Retrieving payment", input);
|
|
207
|
+
}
|
|
208
|
+
const { id } = core.validateRequiredKeys(
|
|
209
|
+
["id"],
|
|
210
|
+
input?.data ?? {},
|
|
211
|
+
"Missing required payment ID"
|
|
212
|
+
);
|
|
213
|
+
if (!id) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
214
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(this.paykit.payments.retrieve(id));
|
|
215
|
+
if (paymentIntentError)
|
|
216
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
217
|
+
return { data: paymentIntentResult };
|
|
218
|
+
};
|
|
219
|
+
updatePayment = async (input) => {
|
|
220
|
+
if (this.options.debug) {
|
|
221
|
+
console.info("[PayKit] Updating payment", input);
|
|
222
|
+
}
|
|
223
|
+
const { amount, currency_code } = core.validateRequiredKeys(
|
|
224
|
+
["amount", "currency_code"],
|
|
225
|
+
input,
|
|
226
|
+
"Missing required payment ID"
|
|
227
|
+
);
|
|
228
|
+
if (!amount || !currency_code)
|
|
229
|
+
throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required amount or currency code");
|
|
230
|
+
const { id: paymentId } = core.validateRequiredKeys(
|
|
231
|
+
["id"],
|
|
232
|
+
input.data,
|
|
233
|
+
"Missing required payment ID"
|
|
234
|
+
);
|
|
235
|
+
if (!paymentId) throw new utils.MedusaError(utils.MedusaError.Types.INVALID_DATA, "Missing required payment ID");
|
|
236
|
+
const [paymentIntentResult, paymentIntentError] = await core.tryCatchAsync(
|
|
237
|
+
this.paykit.payments.update(paymentId, {
|
|
238
|
+
amount: Number(amount),
|
|
239
|
+
currency: currency_code,
|
|
240
|
+
metadata: input.data?.metadata ? input.data.metadata : {},
|
|
241
|
+
provider_metadata: input.data?.provider_metadata ? input.data.provider_metadata : void 0
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
if (paymentIntentError)
|
|
245
|
+
throw new utils.MedusaError(utils.MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR, paymentIntentError.message);
|
|
246
|
+
return { data: paymentIntentResult };
|
|
247
|
+
};
|
|
248
|
+
getWebhookActionAndData = async (payload) => {
|
|
249
|
+
if (this.options.debug) {
|
|
250
|
+
console.info("[PayKit] Getting webhook action and data", payload);
|
|
251
|
+
}
|
|
252
|
+
const { rawData, headers } = payload;
|
|
253
|
+
const fullUrl = (() => {
|
|
254
|
+
if (headers["origin"]) {
|
|
255
|
+
return headers["origin"];
|
|
256
|
+
}
|
|
257
|
+
if (headers["x-forwarded-host"]) {
|
|
258
|
+
const protocol = headers["x-forwarded-proto"] || "https";
|
|
259
|
+
const host = headers["x-forwarded-host"];
|
|
260
|
+
const path = headers["x-forwarded-path"] || "";
|
|
261
|
+
return `${protocol}://${host}${path}`;
|
|
262
|
+
}
|
|
263
|
+
if (headers["host"]) {
|
|
264
|
+
const protocol = headers["x-forwarded-proto"] || (String(headers["host"]).includes("localhost") ? "http" : "https");
|
|
265
|
+
const host = headers["host"];
|
|
266
|
+
const path = headers["x-original-url"] || headers["x-forwarded-path"] || "";
|
|
267
|
+
return `${protocol}://${host}${path}`;
|
|
268
|
+
}
|
|
269
|
+
return "";
|
|
270
|
+
})();
|
|
271
|
+
const webhook = this.paykit.webhooks.setup({ webhookSecret: this.options.webhookSecret }).on("payment.created", async (event) => {
|
|
272
|
+
return {
|
|
273
|
+
action: utils.PaymentActions.PENDING,
|
|
274
|
+
data: { session_id: event.data?.metadata?.session_id, amount: event.data?.amount }
|
|
275
|
+
};
|
|
276
|
+
}).on("payment.updated", async (event) => {
|
|
277
|
+
const statusActionMap = {
|
|
278
|
+
pending: utils.PaymentActions.PENDING,
|
|
279
|
+
processing: utils.PaymentActions.PENDING,
|
|
280
|
+
requires_action: utils.PaymentActions.REQUIRES_MORE,
|
|
281
|
+
requires_capture: utils.PaymentActions.AUTHORIZED,
|
|
282
|
+
succeeded: utils.PaymentActions.SUCCESSFUL,
|
|
283
|
+
failed: utils.PaymentActions.FAILED,
|
|
284
|
+
canceled: utils.PaymentActions.CANCELED
|
|
285
|
+
};
|
|
286
|
+
return {
|
|
287
|
+
action: event.data?.status ? statusActionMap[event.data.status] : utils.PaymentActions.PENDING,
|
|
288
|
+
data: { session_id: event.data?.metadata?.session_id, amount: event.data?.amount }
|
|
289
|
+
};
|
|
290
|
+
}).on("payment.canceled", async (event) => {
|
|
291
|
+
return {
|
|
292
|
+
action: utils.PaymentActions.CANCELED,
|
|
293
|
+
data: { session_id: event.data?.metadata?.session_id, amount: event.data?.amount }
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
const webhookEvents = await webhook.handle({
|
|
297
|
+
body: rawData,
|
|
298
|
+
headers,
|
|
299
|
+
fullUrl
|
|
300
|
+
});
|
|
301
|
+
return webhookEvents;
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/index.ts
|
|
306
|
+
var index_default = utils.ModuleProvider(utils.Modules.PAYMENT, {
|
|
307
|
+
services: [PaykitMedusaJSAdapter]
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
exports.PaykitMedusaJSAdapter = PaykitMedusaJSAdapter;
|
|
311
|
+
exports.default = index_default;
|