medusa-paypal-plugin 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/.medusa/server/src/admin/index.js +23 -0
- package/.medusa/server/src/admin/index.mjs +24 -0
- package/.medusa/server/src/api/admin/plugin/route.js +7 -0
- package/.medusa/server/src/api/store/plugin/route.js +7 -0
- package/.medusa/server/src/api/webhooks/paypal/route.js +16 -0
- package/.medusa/server/src/modules/paypal/index.js +11 -0
- package/.medusa/server/src/modules/paypal/service.js +494 -0
- package/README.md +64 -0
- package/package.json +70 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const i18nTranslations0 = {};
|
|
3
|
+
const widgetModule = { widgets: [] };
|
|
4
|
+
const routeModule = {
|
|
5
|
+
routes: []
|
|
6
|
+
};
|
|
7
|
+
const menuItemModule = {
|
|
8
|
+
menuItems: []
|
|
9
|
+
};
|
|
10
|
+
const formModule = { customFields: {} };
|
|
11
|
+
const displayModule = {
|
|
12
|
+
displays: {}
|
|
13
|
+
};
|
|
14
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
15
|
+
const plugin = {
|
|
16
|
+
widgetModule,
|
|
17
|
+
routeModule,
|
|
18
|
+
menuItemModule,
|
|
19
|
+
formModule,
|
|
20
|
+
displayModule,
|
|
21
|
+
i18nModule
|
|
22
|
+
};
|
|
23
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const i18nTranslations0 = {};
|
|
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: i18nTranslations0 };
|
|
14
|
+
const plugin = {
|
|
15
|
+
widgetModule,
|
|
16
|
+
routeModule,
|
|
17
|
+
menuItemModule,
|
|
18
|
+
formModule,
|
|
19
|
+
displayModule,
|
|
20
|
+
i18nModule
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
plugin as default
|
|
24
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
async function GET(req, res) {
|
|
5
|
+
res.sendStatus(200);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQUtDO0FBTE0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
async function GET(req, res) {
|
|
5
|
+
res.sendStatus(200);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQUtDO0FBTE0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/api/webhooks/paypal/route.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.POST = POST;
|
|
5
|
+
async function POST(req) {
|
|
6
|
+
const body = await req.json();
|
|
7
|
+
if (body.event_type === "PAYMENT.CAPTURE.COMPLETED") {
|
|
8
|
+
console.log("支付成功:", body);
|
|
9
|
+
// TODO:
|
|
10
|
+
// 1. 找到 order
|
|
11
|
+
// 2. mark paid
|
|
12
|
+
// 3. capture medusa payment
|
|
13
|
+
}
|
|
14
|
+
return Response.json({ received: true });
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3dlYmhvb2tzL3BheXBhbC9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsbUNBQW1DOztBQUVuQyxvQkFZQztBQVpNLEtBQUssVUFBVSxJQUFJLENBQUMsR0FBUTtJQUMvQixNQUFNLElBQUksR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUU5QixJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssMkJBQTJCLEVBQUUsQ0FBQztRQUNsRCxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMzQixRQUFRO1FBQ1IsY0FBYztRQUNkLGVBQWU7UUFDZiw0QkFBNEI7SUFDaEMsQ0FBQztJQUVELE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBQzdDLENBQUMifQ==
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
const service_1 = __importDefault(require("./service"));
|
|
7
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
8
|
+
exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.PAYMENT, {
|
|
9
|
+
services: [service_1.default],
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9wYXlwYWwvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx3REFBcUQ7QUFDckQscURBQW9FO0FBRXBFLGtCQUFlLElBQUEsc0JBQWMsRUFBQyxlQUFPLENBQUMsT0FBTyxFQUFFO0lBQzNDLFFBQVEsRUFBRSxDQUFDLGlCQUE0QixDQUFDO0NBQzNDLENBQUMsQ0FBQyJ9
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
4
|
+
const paypal_server_sdk_1 = require("@paypal/paypal-server-sdk");
|
|
5
|
+
class PayPalPaymentProviderService extends utils_1.AbstractPaymentProvider {
|
|
6
|
+
constructor(container, options) {
|
|
7
|
+
super(container, options);
|
|
8
|
+
this.logger_ = container.logger;
|
|
9
|
+
this.options_ = {
|
|
10
|
+
environment: "sandbox",
|
|
11
|
+
autoCapture: false,
|
|
12
|
+
...options,
|
|
13
|
+
};
|
|
14
|
+
// Initialize PayPal client
|
|
15
|
+
this.client_ = new paypal_server_sdk_1.Client({
|
|
16
|
+
environment: this.options_.environment === "production"
|
|
17
|
+
? paypal_server_sdk_1.Environment.Production
|
|
18
|
+
: paypal_server_sdk_1.Environment.Sandbox,
|
|
19
|
+
clientCredentialsAuthCredentials: {
|
|
20
|
+
oAuthClientId: this.options_.client_id,
|
|
21
|
+
oAuthClientSecret: this.options_.client_secret,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
this.ordersController_ = new paypal_server_sdk_1.OrdersController(this.client_);
|
|
25
|
+
this.paymentsController_ = new paypal_server_sdk_1.PaymentsController(this.client_);
|
|
26
|
+
}
|
|
27
|
+
static validateOptions(options) {
|
|
28
|
+
if (!options.client_id) {
|
|
29
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Client ID is required");
|
|
30
|
+
}
|
|
31
|
+
if (!options.client_secret) {
|
|
32
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Client secret is required");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async initiatePayment(input) {
|
|
36
|
+
try {
|
|
37
|
+
const { amount, currency_code } = input;
|
|
38
|
+
console.log("amount:", amount);
|
|
39
|
+
// Determine intent based on capture option
|
|
40
|
+
const intent = this.options_.autoCapture
|
|
41
|
+
? paypal_server_sdk_1.CheckoutPaymentIntent.Capture
|
|
42
|
+
: paypal_server_sdk_1.CheckoutPaymentIntent.Authorize;
|
|
43
|
+
// Create PayPal order request
|
|
44
|
+
const orderRequest = {
|
|
45
|
+
intent: intent,
|
|
46
|
+
purchaseUnits: [
|
|
47
|
+
{
|
|
48
|
+
amount: {
|
|
49
|
+
currencyCode: currency_code.toUpperCase(),
|
|
50
|
+
value: amount.toString(),
|
|
51
|
+
// value: (Number(amount) / 100).toFixed(2),
|
|
52
|
+
},
|
|
53
|
+
description: "Order payment",
|
|
54
|
+
customId: input.data?.session_id,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
applicationContext: {
|
|
58
|
+
// TODO: Customize as needed
|
|
59
|
+
brandName: "Store",
|
|
60
|
+
landingPage: paypal_server_sdk_1.OrderApplicationContextLandingPage.NoPreference,
|
|
61
|
+
userAction: paypal_server_sdk_1.OrderApplicationContextUserAction.PayNow,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const response = await this.ordersController_.createOrder({
|
|
65
|
+
body: orderRequest,
|
|
66
|
+
prefer: "return=representation",
|
|
67
|
+
});
|
|
68
|
+
const order = response.result;
|
|
69
|
+
console.log("paypal order =", order);
|
|
70
|
+
console.log("paypal order id =", order.id);
|
|
71
|
+
if (!order?.id) {
|
|
72
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to create PayPal order");
|
|
73
|
+
}
|
|
74
|
+
// Extract approval URL from links
|
|
75
|
+
const approvalUrl = order.links?.find((link) => link.rel === "approve")?.href;
|
|
76
|
+
return {
|
|
77
|
+
id: order.id,
|
|
78
|
+
data: {
|
|
79
|
+
order_id: order.id,
|
|
80
|
+
intent: intent,
|
|
81
|
+
status: order.status,
|
|
82
|
+
approval_url: approvalUrl,
|
|
83
|
+
session_id: input.data?.session_id,
|
|
84
|
+
currency_code
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to initiate PayPal payment: ${error.result?.message || error}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async authorizePayment(input) {
|
|
93
|
+
try {
|
|
94
|
+
const orderId = input.data?.order_id;
|
|
95
|
+
if (!orderId || typeof orderId !== "string") {
|
|
96
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal order ID is required");
|
|
97
|
+
}
|
|
98
|
+
// If capture is enabled, authorize and capture in one step
|
|
99
|
+
if (this.options_.autoCapture) {
|
|
100
|
+
const response = await this.ordersController_.captureOrder({
|
|
101
|
+
id: orderId,
|
|
102
|
+
prefer: "return=representation",
|
|
103
|
+
});
|
|
104
|
+
const capture = response.result;
|
|
105
|
+
if (!capture?.id) {
|
|
106
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to capture PayPal payment");
|
|
107
|
+
}
|
|
108
|
+
// Extract capture ID from purchase units
|
|
109
|
+
const captureId = capture.purchaseUnits?.[0]?.payments?.captures?.[0]?.id;
|
|
110
|
+
return {
|
|
111
|
+
data: {
|
|
112
|
+
...input.data,
|
|
113
|
+
capture_id: captureId,
|
|
114
|
+
intent: "CAPTURE",
|
|
115
|
+
},
|
|
116
|
+
status: "captured",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Otherwise, just authorize
|
|
120
|
+
const response = await this.ordersController_.authorizeOrder({
|
|
121
|
+
id: orderId,
|
|
122
|
+
prefer: "return=representation",
|
|
123
|
+
});
|
|
124
|
+
const authorization = response.result;
|
|
125
|
+
if (!authorization?.id) {
|
|
126
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to authorize PayPal payment");
|
|
127
|
+
}
|
|
128
|
+
// Extract authorization ID from purchase units
|
|
129
|
+
const authId = authorization.purchaseUnits?.[0]?.payments?.authorizations?.[0]?.id;
|
|
130
|
+
return {
|
|
131
|
+
data: {
|
|
132
|
+
order_id: orderId,
|
|
133
|
+
authorization_id: authId,
|
|
134
|
+
intent: "AUTHORIZE",
|
|
135
|
+
currency_code: input.data?.currency_code,
|
|
136
|
+
},
|
|
137
|
+
status: "authorized",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to authorize PayPal payment: ${error.message || error}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async capturePayment(input) {
|
|
145
|
+
try {
|
|
146
|
+
const authorizationId = input.data?.authorization_id;
|
|
147
|
+
if (!authorizationId || typeof authorizationId !== "string") {
|
|
148
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal authorization ID is required for capture");
|
|
149
|
+
}
|
|
150
|
+
const response = await this.paymentsController_.captureAuthorizedPayment({
|
|
151
|
+
authorizationId: authorizationId,
|
|
152
|
+
prefer: "return=representation",
|
|
153
|
+
});
|
|
154
|
+
const capture = response.result;
|
|
155
|
+
if (!capture?.id) {
|
|
156
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to capture PayPal payment");
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
data: {
|
|
160
|
+
...input.data,
|
|
161
|
+
capture_id: capture.id,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to capture PayPal payment: ${error.result?.message || error}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async refundPayment(input) {
|
|
170
|
+
try {
|
|
171
|
+
const captureId = input.data?.capture_id;
|
|
172
|
+
if (!captureId || typeof captureId !== "string") {
|
|
173
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal capture ID is required for refund");
|
|
174
|
+
}
|
|
175
|
+
const refundRequest = {
|
|
176
|
+
amount: {
|
|
177
|
+
currencyCode: input.data?.currency_code
|
|
178
|
+
?.toUpperCase() || "",
|
|
179
|
+
value: new utils_1.BigNumber(input.amount).numeric.toString(),
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const response = await this.paymentsController_.refundCapturedPayment({
|
|
183
|
+
captureId: captureId,
|
|
184
|
+
body: Object.keys(refundRequest).length > 0 ? refundRequest : undefined,
|
|
185
|
+
prefer: "return=representation",
|
|
186
|
+
});
|
|
187
|
+
const refund = response.result;
|
|
188
|
+
if (!refund?.id) {
|
|
189
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to refund PayPal payment");
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
data: {
|
|
193
|
+
...input.data,
|
|
194
|
+
refund_id: refund.id,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
console.log(error);
|
|
200
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to refund PayPal payment: ${error.result?.message || error}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async updatePayment(input) {
|
|
204
|
+
try {
|
|
205
|
+
const orderId = input.data?.order_id;
|
|
206
|
+
if (!orderId) {
|
|
207
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal order ID is required");
|
|
208
|
+
}
|
|
209
|
+
await this.ordersController_.patchOrder({
|
|
210
|
+
id: orderId,
|
|
211
|
+
body: [
|
|
212
|
+
{
|
|
213
|
+
op: paypal_server_sdk_1.PatchOp.Replace,
|
|
214
|
+
path: "/purchase_units/@reference_id=='default'/amount/value",
|
|
215
|
+
value: new utils_1.BigNumber(input.amount).numeric.toString(),
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
data: {
|
|
221
|
+
...input.data,
|
|
222
|
+
currency_code: input.currency_code,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to update PayPal payment: ${error.result?.message || error}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async deletePayment(input) {
|
|
231
|
+
// Note: PayPal doesn't have a cancelOrder API endpoint
|
|
232
|
+
// Orders can only be voided if they're authorized, which is handled in cancelPayment
|
|
233
|
+
// For orders that haven't been authorized yet, they will expire automatically
|
|
234
|
+
return {
|
|
235
|
+
data: input.data,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async retrievePayment(input) {
|
|
239
|
+
try {
|
|
240
|
+
const orderId = input.data?.order_id;
|
|
241
|
+
if (!orderId || typeof orderId !== "string") {
|
|
242
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal order ID is required");
|
|
243
|
+
}
|
|
244
|
+
const response = await this.ordersController_.getOrder({
|
|
245
|
+
id: orderId,
|
|
246
|
+
});
|
|
247
|
+
const order = response.result;
|
|
248
|
+
if (!order?.id) {
|
|
249
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "PayPal order not found");
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
data: {
|
|
253
|
+
order_id: order.id,
|
|
254
|
+
status: order.status,
|
|
255
|
+
intent: order.intent,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to retrieve PayPal payment: ${error.result?.message || error}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async cancelPayment(input) {
|
|
264
|
+
try {
|
|
265
|
+
const authorizationId = input.data?.authorization_id;
|
|
266
|
+
if (!authorizationId || typeof authorizationId !== "string") {
|
|
267
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal authorization ID is required for cancellation");
|
|
268
|
+
}
|
|
269
|
+
await this.paymentsController_.voidPayment({
|
|
270
|
+
authorizationId: authorizationId,
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
data: input.data,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to cancel PayPal payment: ${error.result?.message || error}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async getPaymentStatus(input) {
|
|
281
|
+
try {
|
|
282
|
+
const orderId = input.data?.order_id;
|
|
283
|
+
if (!orderId || typeof orderId !== "string") {
|
|
284
|
+
return { status: "pending" };
|
|
285
|
+
}
|
|
286
|
+
const response = await this.ordersController_.getOrder({
|
|
287
|
+
id: orderId,
|
|
288
|
+
});
|
|
289
|
+
const order = response.result;
|
|
290
|
+
if (!order) {
|
|
291
|
+
return { status: "pending" };
|
|
292
|
+
}
|
|
293
|
+
const status = order.status;
|
|
294
|
+
switch (status) {
|
|
295
|
+
case paypal_server_sdk_1.OrderStatus.Created:
|
|
296
|
+
case paypal_server_sdk_1.OrderStatus.Saved:
|
|
297
|
+
return { status: "pending" };
|
|
298
|
+
case paypal_server_sdk_1.OrderStatus.Approved:
|
|
299
|
+
return { status: "authorized" };
|
|
300
|
+
case paypal_server_sdk_1.OrderStatus.Completed:
|
|
301
|
+
return { status: "authorized" };
|
|
302
|
+
case paypal_server_sdk_1.OrderStatus.Voided:
|
|
303
|
+
return { status: "canceled" };
|
|
304
|
+
default:
|
|
305
|
+
return { status: "pending" };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
return { status: "pending" };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async verifyWebhookSignature(headers, body, rawBody) {
|
|
313
|
+
try {
|
|
314
|
+
if (!this.options_.webhook_id) {
|
|
315
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "PayPal webhook ID is required for webhook signature verification");
|
|
316
|
+
}
|
|
317
|
+
const transmissionId = headers["paypal-transmission-id"];
|
|
318
|
+
const transmissionTime = headers["paypal-transmission-time"];
|
|
319
|
+
const certUrl = headers["paypal-cert-url"];
|
|
320
|
+
const authAlgo = headers["paypal-auth-algo"];
|
|
321
|
+
const transmissionSig = headers["paypal-transmission-sig"];
|
|
322
|
+
if (!transmissionId ||
|
|
323
|
+
!transmissionTime ||
|
|
324
|
+
!certUrl ||
|
|
325
|
+
!authAlgo ||
|
|
326
|
+
!transmissionSig) {
|
|
327
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Missing required PayPal webhook headers");
|
|
328
|
+
}
|
|
329
|
+
// PayPal's API endpoint for webhook verification
|
|
330
|
+
const baseUrl = this.options_.environment === "production"
|
|
331
|
+
? "https://api.paypal.com"
|
|
332
|
+
: "https://api.sandbox.paypal.com";
|
|
333
|
+
const verifyUrl = `${baseUrl}/v1/notifications/verify-webhook-signature`;
|
|
334
|
+
// Get access token for verification API call
|
|
335
|
+
const authResponse = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
336
|
+
method: "POST",
|
|
337
|
+
headers: {
|
|
338
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
339
|
+
Authorization: `Basic ${Buffer.from(`${this.options_.client_id}:${this.options_.client_secret}`).toString("base64")}`,
|
|
340
|
+
},
|
|
341
|
+
body: "grant_type=client_credentials",
|
|
342
|
+
});
|
|
343
|
+
if (!authResponse.ok) {
|
|
344
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to get access token for webhook verification");
|
|
345
|
+
}
|
|
346
|
+
const authData = await authResponse.json();
|
|
347
|
+
const accessToken = authData.access_token;
|
|
348
|
+
if (!accessToken) {
|
|
349
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Access token not received from PayPal");
|
|
350
|
+
}
|
|
351
|
+
let webhookEvent;
|
|
352
|
+
if (rawBody) {
|
|
353
|
+
const rawBodyString = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
354
|
+
try {
|
|
355
|
+
webhookEvent = JSON.parse(rawBodyString);
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
this.logger_.warn("Raw body is not valid JSON, using parsed body");
|
|
359
|
+
webhookEvent = body;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
this.logger_.warn("Raw body not available, using parsed body. Verification may fail if formatting differs.");
|
|
364
|
+
webhookEvent = body;
|
|
365
|
+
}
|
|
366
|
+
const verifyPayload = {
|
|
367
|
+
transmission_id: transmissionId,
|
|
368
|
+
transmission_time: transmissionTime,
|
|
369
|
+
cert_url: certUrl,
|
|
370
|
+
auth_algo: authAlgo,
|
|
371
|
+
transmission_sig: transmissionSig,
|
|
372
|
+
webhook_id: this.options_.webhook_id,
|
|
373
|
+
webhook_event: webhookEvent,
|
|
374
|
+
};
|
|
375
|
+
const verifyResponse = await fetch(verifyUrl, {
|
|
376
|
+
method: "POST",
|
|
377
|
+
headers: {
|
|
378
|
+
"Content-Type": "application/json",
|
|
379
|
+
Authorization: `Bearer ${accessToken}`,
|
|
380
|
+
},
|
|
381
|
+
body: JSON.stringify(verifyPayload),
|
|
382
|
+
});
|
|
383
|
+
if (!verifyResponse.ok) {
|
|
384
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Webhook verification API call failed");
|
|
385
|
+
}
|
|
386
|
+
const verifyData = await verifyResponse.json();
|
|
387
|
+
// PayPal returns verification_status: "SUCCESS" if verification passes
|
|
388
|
+
const isValid = verifyData.verification_status === "SUCCESS";
|
|
389
|
+
if (!isValid) {
|
|
390
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Webhook signature verification failed");
|
|
391
|
+
}
|
|
392
|
+
return isValid;
|
|
393
|
+
}
|
|
394
|
+
catch (e) {
|
|
395
|
+
this.logger_.error("PayPal verifyWebhookSignature error:", e);
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async getWebhookActionAndData(payload) {
|
|
400
|
+
try {
|
|
401
|
+
const { data, rawData, headers } = payload;
|
|
402
|
+
// Verify webhook signature
|
|
403
|
+
const isValid = await this.verifyWebhookSignature(headers || {}, data, rawData || "");
|
|
404
|
+
if (!isValid) {
|
|
405
|
+
this.logger_.error("Invalid PayPal webhook signature");
|
|
406
|
+
return {
|
|
407
|
+
action: "failed",
|
|
408
|
+
data: {
|
|
409
|
+
session_id: "",
|
|
410
|
+
amount: new utils_1.BigNumber(0),
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
// PayPal webhook events have event_type
|
|
415
|
+
const eventType = data?.event_type;
|
|
416
|
+
if (!eventType) {
|
|
417
|
+
this.logger_.warn("PayPal webhook event missing event_type");
|
|
418
|
+
return {
|
|
419
|
+
action: "not_supported",
|
|
420
|
+
data: {
|
|
421
|
+
session_id: "",
|
|
422
|
+
amount: new utils_1.BigNumber(0),
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
// Extract order ID and amount from webhook payload
|
|
427
|
+
const resource = data?.resource;
|
|
428
|
+
let sessionId = data?.resource?.custom_id;
|
|
429
|
+
if (!sessionId) {
|
|
430
|
+
this.logger_.warn("Session ID not found in PayPal webhook resource");
|
|
431
|
+
return {
|
|
432
|
+
action: "not_supported",
|
|
433
|
+
data: {
|
|
434
|
+
session_id: "",
|
|
435
|
+
amount: new utils_1.BigNumber(0),
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
const amountValue = resource?.amount?.value ||
|
|
440
|
+
resource?.purchase_units?.[0]?.payments?.captures?.[0]?.amount
|
|
441
|
+
?.value ||
|
|
442
|
+
resource?.purchase_units?.[0]?.payments?.authorizations?.[0]
|
|
443
|
+
?.amount?.value ||
|
|
444
|
+
0;
|
|
445
|
+
const amount = new utils_1.BigNumber(amountValue);
|
|
446
|
+
const payloadData = {
|
|
447
|
+
session_id: sessionId,
|
|
448
|
+
amount,
|
|
449
|
+
};
|
|
450
|
+
// Map PayPal webhook events to Medusa actions
|
|
451
|
+
switch (eventType) {
|
|
452
|
+
case "PAYMENT.AUTHORIZATION.CREATED":
|
|
453
|
+
return {
|
|
454
|
+
action: utils_1.PaymentActions.AUTHORIZED,
|
|
455
|
+
data: payloadData,
|
|
456
|
+
};
|
|
457
|
+
case "PAYMENT.CAPTURE.DENIED":
|
|
458
|
+
return {
|
|
459
|
+
action: utils_1.PaymentActions.FAILED,
|
|
460
|
+
data: payloadData,
|
|
461
|
+
};
|
|
462
|
+
case "PAYMENT.AUTHORIZATION.VOIDED":
|
|
463
|
+
return {
|
|
464
|
+
action: utils_1.PaymentActions.CANCELED,
|
|
465
|
+
data: payloadData,
|
|
466
|
+
};
|
|
467
|
+
case "PAYMENT.CAPTURE.COMPLETED":
|
|
468
|
+
return {
|
|
469
|
+
action: utils_1.PaymentActions.SUCCESSFUL,
|
|
470
|
+
data: payloadData,
|
|
471
|
+
};
|
|
472
|
+
default:
|
|
473
|
+
this.logger_.info(`Unhandled PayPal webhook event: ${eventType}`);
|
|
474
|
+
return {
|
|
475
|
+
action: utils_1.PaymentActions.NOT_SUPPORTED,
|
|
476
|
+
data: payloadData,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
this.logger_.error("PayPal getWebhookActionAndData error:", error.result?.message || error);
|
|
482
|
+
return {
|
|
483
|
+
action: "failed",
|
|
484
|
+
data: {
|
|
485
|
+
session_id: "",
|
|
486
|
+
amount: new utils_1.BigNumber(0),
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
PayPalPaymentProviderService.identifier = "paypal";
|
|
493
|
+
exports.default = PayPalPaymentProviderService;
|
|
494
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL3BheXBhbC9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscURBQTJHO0FBRTNHLGlFQVVrQztBQXNDbEMsTUFBTSw0QkFBNkIsU0FBUSwrQkFBZ0M7SUFTdkUsWUFBWSxTQUErQixFQUFFLE9BQWdCO1FBQ3pELEtBQUssQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFFekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFBO1FBQy9CLElBQUksQ0FBQyxRQUFRLEdBQUc7WUFDWixXQUFXLEVBQUUsU0FBUztZQUN0QixXQUFXLEVBQUUsS0FBSztZQUNsQixHQUFHLE9BQU87U0FDYixDQUFBO1FBRUQsMkJBQTJCO1FBQzNCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSwwQkFBTSxDQUFDO1lBQ3RCLFdBQVcsRUFDUCxJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsS0FBSyxZQUFZO2dCQUN0QyxDQUFDLENBQUMsK0JBQVcsQ0FBQyxVQUFVO2dCQUN4QixDQUFDLENBQUMsK0JBQVcsQ0FBQyxPQUFPO1lBQzdCLGdDQUFnQyxFQUFFO2dCQUM5QixhQUFhLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTO2dCQUN0QyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWE7YUFDakQ7U0FDSixDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxvQ0FBZ0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDM0QsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksc0NBQWtCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ25FLENBQUM7SUFFRCxNQUFNLENBQUMsZUFBZSxDQUFDLE9BQXlCO1FBQzVDLElBQUksQ0FBQyxPQUFPLENBQUMs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
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.medusajs.com">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
|
|
6
|
+
<img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
|
|
7
|
+
</picture>
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
<h1 align="center">
|
|
11
|
+
Medusa Plugin Starter
|
|
12
|
+
</h1>
|
|
13
|
+
|
|
14
|
+
<h4 align="center">
|
|
15
|
+
<a href="https://docs.medusajs.com">Documentation</a> |
|
|
16
|
+
<a href="https://www.medusajs.com">Website</a>
|
|
17
|
+
</h4>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
Building blocks for digital commerce
|
|
21
|
+
</p>
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
|
|
24
|
+
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
|
|
25
|
+
</a>
|
|
26
|
+
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
|
|
27
|
+
<a href="https://discord.gg/xpCwq3Kfn8">
|
|
28
|
+
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
|
|
29
|
+
</a>
|
|
30
|
+
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
|
|
31
|
+
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
|
|
32
|
+
</a>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
## Compatibility
|
|
36
|
+
|
|
37
|
+
This starter is compatible with versions >= 2.4.0 of `@medusajs/medusa`.
|
|
38
|
+
|
|
39
|
+
## Getting Started
|
|
40
|
+
|
|
41
|
+
Visit the [Quickstart Guide](https://docs.medusajs.com/learn/installation) to set up a server.
|
|
42
|
+
|
|
43
|
+
Visit the [Plugins documentation](https://docs.medusajs.com/learn/fundamentals/plugins) to learn more about plugins and how to create them.
|
|
44
|
+
|
|
45
|
+
Visit the [Docs](https://docs.medusajs.com/learn/installation#get-started) to learn more about our system requirements.
|
|
46
|
+
|
|
47
|
+
## What is Medusa
|
|
48
|
+
|
|
49
|
+
Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.
|
|
50
|
+
|
|
51
|
+
Learn more about [Medusa’s architecture](https://docs.medusajs.com/learn/introduction/architecture) and [commerce modules](https://docs.medusajs.com/learn/fundamentals/modules/commerce-modules) in the Docs.
|
|
52
|
+
|
|
53
|
+
## Community & Contributions
|
|
54
|
+
|
|
55
|
+
The community and core team are available in [GitHub Discussions](https://github.com/medusajs/medusa/discussions), where you can ask for support, discuss roadmap, and share ideas.
|
|
56
|
+
|
|
57
|
+
Join our [Discord server](https://discord.com/invite/medusajs) to meet other community members.
|
|
58
|
+
|
|
59
|
+
## Other channels
|
|
60
|
+
|
|
61
|
+
- [GitHub Issues](https://github.com/medusajs/medusa/issues)
|
|
62
|
+
- [Twitter](https://twitter.com/medusajs)
|
|
63
|
+
- [LinkedIn](https://www.linkedin.com/company/medusajs)
|
|
64
|
+
- [Medusa Blog](https://medusajs.com/blog/)
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "medusa-paypal-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A starter for Medusa plugins.",
|
|
5
|
+
"author": "Medusa (https://medusajs.com)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
".medusa/server"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
"./workflows": "./.medusa/server/src/workflows/index.js",
|
|
13
|
+
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
14
|
+
"./modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
15
|
+
"./providers/*": "./.medusa/server/src/providers/*/index.js",
|
|
16
|
+
"./*": "./.medusa/server/src/*.js",
|
|
17
|
+
"./admin": {
|
|
18
|
+
"import": "./.medusa/server/src/admin/index.mjs",
|
|
19
|
+
"require": "./.medusa/server/src/admin/index.js",
|
|
20
|
+
"default": "./.medusa/server/src/admin/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"medusa",
|
|
25
|
+
"plugin",
|
|
26
|
+
"medusa-plugin-other",
|
|
27
|
+
"medusa-plugin",
|
|
28
|
+
"medusa-v2"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "medusa plugin:build",
|
|
32
|
+
"dev": "medusa plugin:develop",
|
|
33
|
+
"prepublishOnly": "medusa plugin:build"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@medusajs/admin-sdk": "2.15.3",
|
|
37
|
+
"@medusajs/cli": "2.15.3",
|
|
38
|
+
"@medusajs/framework": "2.15.3",
|
|
39
|
+
"@medusajs/icons": "2.15.3",
|
|
40
|
+
"@medusajs/medusa": "2.15.3",
|
|
41
|
+
"@medusajs/test-utils": "2.15.3",
|
|
42
|
+
"@medusajs/ui": "4.1.13",
|
|
43
|
+
"@swc/core": "^1.7.28",
|
|
44
|
+
"@types/node": "^20.12.11",
|
|
45
|
+
"@types/react": "^18.3.2",
|
|
46
|
+
"@types/react-dom": "^18.2.25",
|
|
47
|
+
"prop-types": "^15.8.1",
|
|
48
|
+
"react": "^18.2.0",
|
|
49
|
+
"react-dom": "^18.2.0",
|
|
50
|
+
"ts-node": "^10.9.2",
|
|
51
|
+
"typescript": "^5.6.2",
|
|
52
|
+
"vite": "^5.4.14",
|
|
53
|
+
"yalc": "^1.0.0-pre.53"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@medusajs/admin-sdk": "2.15.3",
|
|
57
|
+
"@medusajs/cli": "2.15.3",
|
|
58
|
+
"@medusajs/framework": "2.15.3",
|
|
59
|
+
"@medusajs/icons": "2.15.3",
|
|
60
|
+
"@medusajs/medusa": "2.15.3",
|
|
61
|
+
"@medusajs/test-utils": "2.15.3",
|
|
62
|
+
"@medusajs/ui": "4.1.13"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=20"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@paypal/paypal-server-sdk": "^2.3.0"
|
|
69
|
+
}
|
|
70
|
+
}
|