@unchainedshop/plugins 4.6.0 → 4.6.2
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/lib/delivery/post/adapter.d.ts +2 -0
- package/lib/delivery/post/adapter.js +22 -0
- package/lib/delivery/post/index.d.ts +4 -0
- package/lib/delivery/post/index.js +10 -0
- package/lib/delivery/send-message/adapter.d.ts +2 -0
- package/lib/delivery/send-message/adapter.js +39 -0
- package/lib/delivery/send-message/index.d.ts +4 -0
- package/lib/delivery/send-message/index.js +10 -0
- package/lib/delivery/stores/adapter.d.ts +2 -0
- package/lib/delivery/stores/adapter.js +42 -0
- package/lib/delivery/stores/index.d.ts +4 -0
- package/lib/delivery/stores/index.js +10 -0
- package/lib/enrollments/licensed/adapter.d.ts +6 -0
- package/lib/enrollments/licensed/adapter.js +51 -0
- package/lib/enrollments/licensed/index.d.ts +4 -0
- package/lib/enrollments/licensed/index.js +10 -0
- package/lib/files/gridfs/api.d.ts +7 -0
- package/lib/files/gridfs/api.js +163 -0
- package/lib/files/minio/adapter.d.ts +4 -0
- package/lib/files/minio/adapter.js +160 -0
- package/lib/files/minio/api.d.ts +2 -0
- package/lib/files/minio/api.js +41 -0
- package/lib/files/temp-upload/api.d.ts +6 -0
- package/lib/files/temp-upload/api.js +49 -0
- package/lib/files/temp-upload/index.d.ts +3 -0
- package/lib/files/temp-upload/index.js +15 -0
- package/lib/filters/local-search/adapter.d.ts +2 -0
- package/lib/filters/local-search/adapter.js +56 -0
- package/lib/filters/local-search/index.d.ts +4 -0
- package/lib/filters/local-search/index.js +16 -0
- package/lib/filters/strict-equal/adapter.d.ts +2 -0
- package/lib/filters/strict-equal/adapter.js +23 -0
- package/lib/filters/strict-equal/index.d.ts +4 -0
- package/lib/filters/strict-equal/index.js +10 -0
- package/lib/payment/apple-iap/api.d.ts +2 -0
- package/lib/payment/apple-iap/api.js +119 -0
- package/lib/payment/cryptopay/adapter.d.ts +2 -0
- package/lib/payment/cryptopay/adapter.js +157 -0
- package/lib/payment/cryptopay/api.d.ts +2 -0
- package/lib/payment/cryptopay/api.js +23 -0
- package/lib/payment/datatrans-v2/adapter.d.ts +2 -0
- package/lib/payment/datatrans-v2/adapter.js +406 -0
- package/lib/payment/datatrans-v2/api.d.ts +2 -0
- package/lib/payment/datatrans-v2/api.js +108 -0
- package/lib/payment/invoice/adapter.d.ts +2 -0
- package/lib/payment/invoice/adapter.js +26 -0
- package/lib/payment/invoice/index.d.ts +4 -0
- package/lib/payment/invoice/index.js +10 -0
- package/lib/payment/invoice-prepaid/adapter.d.ts +2 -0
- package/lib/payment/invoice-prepaid/adapter.js +29 -0
- package/lib/payment/invoice-prepaid/index.d.ts +4 -0
- package/lib/payment/invoice-prepaid/index.js +10 -0
- package/lib/payment/payrexx/adapter.d.ts +2 -0
- package/lib/payment/payrexx/adapter.js +161 -0
- package/lib/payment/payrexx/api.d.ts +2 -0
- package/lib/payment/payrexx/api.js +97 -0
- package/lib/payment/postfinance-checkout/adapter.d.ts +2 -0
- package/lib/payment/postfinance-checkout/adapter.js +189 -0
- package/lib/payment/postfinance-checkout/webhook.d.ts +2 -0
- package/lib/payment/postfinance-checkout/webhook.js +48 -0
- package/lib/payment/saferpay/webhook.d.ts +2 -0
- package/lib/payment/saferpay/webhook.js +64 -0
- package/lib/payment/stripe/adapter.d.ts +2 -0
- package/lib/payment/stripe/adapter.js +121 -0
- package/lib/payment/stripe/api.d.ts +6 -0
- package/lib/payment/stripe/api.js +165 -0
- package/lib/payment/stripe/stripe.js +12 -10
- package/lib/presets/base.d.ts +1 -0
- package/lib/presets/base.js +1 -0
- package/lib/pricing/delivery-swiss-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-swiss-tax/adapter.js +62 -0
- package/lib/pricing/delivery-swiss-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-swiss-tax/index.js +10 -0
- package/lib/pricing/discount-100-off/adapter.d.ts +3 -0
- package/lib/pricing/discount-100-off/adapter.js +30 -0
- package/lib/pricing/discount-100-off/index.d.ts +4 -0
- package/lib/pricing/discount-100-off/index.js +10 -0
- package/lib/pricing/discount-half-price-manual/adapter.d.ts +3 -0
- package/lib/pricing/discount-half-price-manual/adapter.js +30 -0
- package/lib/pricing/discount-half-price-manual/index.d.ts +4 -0
- package/lib/pricing/discount-half-price-manual/index.js +10 -0
- package/lib/pricing/free-delivery/adapter.d.ts +3 -0
- package/lib/pricing/free-delivery/adapter.js +27 -0
- package/lib/pricing/free-delivery/index.d.ts +4 -0
- package/lib/pricing/free-delivery/index.js +10 -0
- package/lib/pricing/free-payment/adapter.d.ts +3 -0
- package/lib/pricing/free-payment/adapter.js +27 -0
- package/lib/pricing/free-payment/index.d.ts +4 -0
- package/lib/pricing/free-payment/index.js +10 -0
- package/lib/pricing/order-delivery/adapter.d.ts +3 -0
- package/lib/pricing/order-delivery/adapter.js +33 -0
- package/lib/pricing/order-delivery/index.d.ts +4 -0
- package/lib/pricing/order-delivery/index.js +10 -0
- package/lib/pricing/order-discount/adapter.d.ts +3 -0
- package/lib/pricing/order-discount/adapter.js +74 -0
- package/lib/pricing/order-discount/index.d.ts +4 -0
- package/lib/pricing/order-discount/index.js +10 -0
- package/lib/pricing/order-items/adapter.d.ts +3 -0
- package/lib/pricing/order-items/adapter.js +43 -0
- package/lib/pricing/order-items/index.d.ts +4 -0
- package/lib/pricing/order-items/index.js +10 -0
- package/lib/pricing/order-payment/adapter.d.ts +3 -0
- package/lib/pricing/order-payment/adapter.js +33 -0
- package/lib/pricing/order-payment/index.d.ts +4 -0
- package/lib/pricing/order-payment/index.js +10 -0
- package/lib/pricing/product-catalog-price/adapter.d.ts +3 -0
- package/lib/pricing/product-catalog-price/adapter.js +60 -0
- package/lib/pricing/product-catalog-price/index.d.ts +4 -0
- package/lib/pricing/product-catalog-price/index.js +10 -0
- package/lib/pricing/product-discount/adapter.d.ts +3 -0
- package/lib/pricing/product-discount/adapter.js +69 -0
- package/lib/pricing/product-discount/index.d.ts +4 -0
- package/lib/pricing/product-discount/index.js +10 -0
- package/lib/pricing/product-price-rateconversion/adapter.d.ts +3 -0
- package/lib/pricing/product-price-rateconversion/adapter.js +53 -0
- package/lib/pricing/product-price-rateconversion/index.d.ts +4 -0
- package/lib/pricing/product-price-rateconversion/index.js +10 -0
- package/lib/pricing/product-swiss-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-swiss-tax/adapter.js +76 -0
- package/lib/pricing/product-swiss-tax/index.d.ts +4 -0
- package/lib/pricing/product-swiss-tax/index.js +10 -0
- package/lib/quotations/manual/adapter.d.ts +2 -0
- package/lib/quotations/manual/adapter.js +19 -0
- package/lib/quotations/manual/index.d.ts +4 -0
- package/lib/quotations/manual/index.js +10 -0
- package/lib/warehousing/erc-metadata/api.d.ts +12 -0
- package/lib/warehousing/erc-metadata/api.js +43 -0
- package/lib/warehousing/erc-metadata/index.d.ts +3 -0
- package/lib/warehousing/erc-metadata/index.js +15 -0
- package/lib/warehousing/eth-minter/adapter.d.ts +2 -0
- package/lib/warehousing/eth-minter/adapter.js +119 -0
- package/lib/warehousing/eth-minter/index.d.ts +4 -0
- package/lib/warehousing/eth-minter/index.js +10 -0
- package/lib/warehousing/store/adapter.d.ts +2 -0
- package/lib/warehousing/store/adapter.js +33 -0
- package/lib/warehousing/store/index.d.ts +4 -0
- package/lib/warehousing/store/index.js +10 -0
- package/lib/worker/budgetsms/adapter.d.ts +22 -0
- package/lib/worker/budgetsms/adapter.js +169 -0
- package/lib/worker/budgetsms/index.d.ts +4 -0
- package/lib/worker/budgetsms/index.js +10 -0
- package/lib/worker/bulk-export/adapter.d.ts +3 -0
- package/lib/worker/bulk-export/adapter.js +64 -0
- package/lib/worker/bulk-export/index.d.ts +4 -0
- package/lib/worker/bulk-export/index.js +10 -0
- package/lib/worker/bulk-export.d.ts +2 -0
- package/lib/worker/bulk-export.js +64 -0
- package/lib/worker/bulk-import/adapter.d.ts +3 -0
- package/lib/worker/bulk-import/adapter.js +65 -0
- package/lib/worker/bulk-import/api.d.ts +7 -0
- package/lib/worker/bulk-import/api.js +53 -0
- package/lib/worker/bulk-import/index.d.ts +4 -0
- package/lib/worker/bulk-import/index.js +18 -0
- package/lib/worker/bulkgate/adapter.d.ts +12 -0
- package/lib/worker/bulkgate/adapter.js +91 -0
- package/lib/worker/bulkgate/index.d.ts +4 -0
- package/lib/worker/bulkgate/index.js +10 -0
- package/lib/worker/email/adapter.d.ts +9 -0
- package/lib/worker/email/adapter.js +150 -0
- package/lib/worker/email/index.d.ts +4 -0
- package/lib/worker/email/index.js +10 -0
- package/lib/worker/enrollment-order-generator/adapter.d.ts +4 -0
- package/lib/worker/enrollment-order-generator/adapter.js +77 -0
- package/lib/worker/enrollment-order-generator/index.d.ts +4 -0
- package/lib/worker/enrollment-order-generator/index.js +13 -0
- package/lib/worker/error-notifications/adapter.d.ts +10 -0
- package/lib/worker/error-notifications/adapter.js +62 -0
- package/lib/worker/error-notifications/index.d.ts +4 -0
- package/lib/worker/error-notifications/index.js +13 -0
- package/lib/worker/export-token/adapter.d.ts +4 -0
- package/lib/worker/export-token/adapter.js +25 -0
- package/lib/worker/export-token/index.d.ts +4 -0
- package/lib/worker/export-token/index.js +13 -0
- package/lib/worker/external/adapter.d.ts +3 -0
- package/lib/worker/external/adapter.js +13 -0
- package/lib/worker/external/index.d.ts +4 -0
- package/lib/worker/external/index.js +10 -0
- package/lib/worker/heartbeat/adapter.d.ts +8 -0
- package/lib/worker/heartbeat/adapter.js +26 -0
- package/lib/worker/heartbeat/index.d.ts +4 -0
- package/lib/worker/heartbeat/index.js +10 -0
- package/lib/worker/http-request/adapter.d.ts +8 -0
- package/lib/worker/http-request/adapter.js +55 -0
- package/lib/worker/http-request/index.d.ts +4 -0
- package/lib/worker/http-request/index.js +10 -0
- package/lib/worker/message/adapter.d.ts +11 -0
- package/lib/worker/message/adapter.js +42 -0
- package/lib/worker/message/index.d.ts +4 -0
- package/lib/worker/message/index.js +10 -0
- package/lib/worker/push-notification/adapter.d.ts +9 -0
- package/lib/worker/push-notification/adapter.js +87 -0
- package/lib/worker/push-notification/index.d.ts +4 -0
- package/lib/worker/push-notification/index.js +10 -0
- package/lib/worker/twilio/adapter.d.ts +8 -0
- package/lib/worker/twilio/adapter.js +59 -0
- package/lib/worker/twilio/index.d.ts +4 -0
- package/lib/worker/twilio/index.js +10 -0
- package/lib/worker/update-coinbase-rates/adapter.d.ts +4 -0
- package/lib/worker/update-coinbase-rates/adapter.js +61 -0
- package/lib/worker/update-coinbase-rates/index.d.ts +4 -0
- package/lib/worker/update-coinbase-rates/index.js +13 -0
- package/lib/worker/update-ecb-rates/adapter.d.ts +4 -0
- package/lib/worker/update-ecb-rates/adapter.js +94 -0
- package/lib/worker/update-ecb-rates/index.d.ts +4 -0
- package/lib/worker/update-ecb-rates/index.js +13 -0
- package/lib/worker/update-token-ownership/adapter.d.ts +5 -0
- package/lib/worker/update-token-ownership/adapter.js +58 -0
- package/lib/worker/update-token-ownership/index.d.ts +4 -0
- package/lib/worker/update-token-ownership/index.js +13 -0
- package/lib/worker/zombie-killer/adapter.d.ts +13 -0
- package/lib/worker/zombie-killer/adapter.js +90 -0
- package/lib/worker/zombie-killer/index.d.ts +4 -0
- package/lib/worker/zombie-killer/index.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { stripe, createOrderPaymentIntent, createRegistrationIntent } from "./stripe.js";
|
|
3
|
+
import { OrderPricingSheet, PaymentAdapter, PaymentError, } from '@unchainedshop/core';
|
|
4
|
+
const logger = createLogger('unchained:stripe');
|
|
5
|
+
export const Stripe = {
|
|
6
|
+
...PaymentAdapter,
|
|
7
|
+
key: 'shop.unchained.payment.stripe',
|
|
8
|
+
label: 'Stripe',
|
|
9
|
+
version: '2.0.0',
|
|
10
|
+
typeSupported(type) {
|
|
11
|
+
return type === 'GENERIC';
|
|
12
|
+
},
|
|
13
|
+
actions: (config, context) => {
|
|
14
|
+
const { modules } = context;
|
|
15
|
+
const descriptorPrefix = config.find(({ key }) => key === 'descriptorPrefix')?.value || '';
|
|
16
|
+
const assertUserData = async (forcedUserId) => {
|
|
17
|
+
const userId = forcedUserId || context?.userId;
|
|
18
|
+
const user = await modules.users.findUserById(userId);
|
|
19
|
+
if (!user)
|
|
20
|
+
throw new Error('User not found');
|
|
21
|
+
const email = modules.users.primaryEmail(user)?.address;
|
|
22
|
+
const name = user.profile?.displayName || user.username || email;
|
|
23
|
+
return {
|
|
24
|
+
email,
|
|
25
|
+
name,
|
|
26
|
+
userId,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const adapterActions = {
|
|
30
|
+
...PaymentAdapter.actions(config, context),
|
|
31
|
+
configurationError() {
|
|
32
|
+
if (!stripe)
|
|
33
|
+
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
34
|
+
return null;
|
|
35
|
+
},
|
|
36
|
+
isActive: () => {
|
|
37
|
+
if (adapterActions.configurationError() === null)
|
|
38
|
+
return true;
|
|
39
|
+
return false;
|
|
40
|
+
},
|
|
41
|
+
isPayLaterAllowed() {
|
|
42
|
+
return false;
|
|
43
|
+
},
|
|
44
|
+
validate: async ({ token }) => {
|
|
45
|
+
const paymentMethod = await stripe.paymentMethods.retrieve(token);
|
|
46
|
+
return !!paymentMethod;
|
|
47
|
+
},
|
|
48
|
+
register: async ({ setupIntentId }) => {
|
|
49
|
+
if (!setupIntentId) {
|
|
50
|
+
throw new Error('You have to provide a setupIntentId');
|
|
51
|
+
}
|
|
52
|
+
const setupIntent = await stripe.setupIntents.retrieve(setupIntentId);
|
|
53
|
+
if (setupIntent.status === 'succeeded') {
|
|
54
|
+
return {
|
|
55
|
+
token: setupIntent.payment_method,
|
|
56
|
+
customer: setupIntent.customer,
|
|
57
|
+
payment_method_types: setupIntent.payment_method_types,
|
|
58
|
+
usage: setupIntent.usage,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
logger.warn('Registration declined', setupIntentId);
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
64
|
+
sign: async (transactionContext = {}) => {
|
|
65
|
+
const { orderPayment, order, paymentProvider } = context;
|
|
66
|
+
const { userId, name, email } = await assertUserData(order?.userId);
|
|
67
|
+
if (orderPayment) {
|
|
68
|
+
if (!order)
|
|
69
|
+
throw new Error('order not found in context');
|
|
70
|
+
const pricing = OrderPricingSheet({
|
|
71
|
+
calculation: order?.calculation,
|
|
72
|
+
currencyCode: order?.currencyCode,
|
|
73
|
+
});
|
|
74
|
+
const paymentIntent = await createOrderPaymentIntent({ userId, name, email, order, orderPayment, pricing, descriptorPrefix }, transactionContext);
|
|
75
|
+
return paymentIntent.client_secret;
|
|
76
|
+
}
|
|
77
|
+
const paymentIntent = await createRegistrationIntent({ userId, name, email, paymentProviderId: paymentProvider._id, descriptorPrefix }, transactionContext);
|
|
78
|
+
return paymentIntent.client_secret;
|
|
79
|
+
},
|
|
80
|
+
charge: async ({ paymentIntentId, paymentCredentials }) => {
|
|
81
|
+
if (!paymentIntentId && !paymentCredentials) {
|
|
82
|
+
throw new Error('You have to provide paymentIntentId or paymentCredentials');
|
|
83
|
+
}
|
|
84
|
+
const { order, orderPayment } = context;
|
|
85
|
+
if (!order)
|
|
86
|
+
throw new Error('order not found in context');
|
|
87
|
+
if (!orderPayment)
|
|
88
|
+
throw new Error('orderPayment not found in context');
|
|
89
|
+
const { userId, name, email } = await assertUserData(order?.userId);
|
|
90
|
+
const pricing = OrderPricingSheet({
|
|
91
|
+
calculation: order.calculation,
|
|
92
|
+
currencyCode: order.currencyCode,
|
|
93
|
+
});
|
|
94
|
+
const paymentIntentObject = paymentIntentId
|
|
95
|
+
? await stripe.paymentIntents.retrieve(paymentIntentId)
|
|
96
|
+
: await createOrderPaymentIntent({ userId, name, email, orderPayment, order, pricing, descriptorPrefix }, {
|
|
97
|
+
customer: paymentCredentials.meta?.customer,
|
|
98
|
+
confirm: true,
|
|
99
|
+
payment_method: paymentCredentials.token,
|
|
100
|
+
payment_method_types: paymentCredentials.meta?.payment_method_types,
|
|
101
|
+
});
|
|
102
|
+
const { currencyCode, amount } = pricing.total({ useNetPrice: false });
|
|
103
|
+
if (paymentIntentObject.currency !== currencyCode.toLowerCase() ||
|
|
104
|
+
paymentIntentObject.amount !== Math.round(amount)) {
|
|
105
|
+
throw new Error('The price has changed since the intent has been created');
|
|
106
|
+
}
|
|
107
|
+
if (paymentIntentObject.metadata?.orderPaymentId !== orderPayment?._id) {
|
|
108
|
+
throw new Error('The order payment is different from the initiating intent');
|
|
109
|
+
}
|
|
110
|
+
if (paymentIntentObject.status === 'succeeded') {
|
|
111
|
+
return paymentIntentObject;
|
|
112
|
+
}
|
|
113
|
+
logger.info('Charge postponed because paymentIntent has wrong status', {
|
|
114
|
+
orderPaymentId: paymentIntentObject.id,
|
|
115
|
+
});
|
|
116
|
+
return false;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
return adapterActions;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UnchainedCore } from '@unchainedshop/core';
|
|
2
|
+
export declare const WebhookEventTypes: {
|
|
3
|
+
PAYMENT_INTENT_SUCCEEDED: string;
|
|
4
|
+
SETUP_INTENT_SUCCEEDED: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function stripeWebhookHandler(request: Request, context: UnchainedCore): Promise<Response>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { stripe } from "./stripe.js";
|
|
3
|
+
const logger = createLogger('unchained:stripe:handler');
|
|
4
|
+
export const WebhookEventTypes = {
|
|
5
|
+
PAYMENT_INTENT_SUCCEEDED: 'payment_intent.succeeded',
|
|
6
|
+
SETUP_INTENT_SUCCEEDED: 'setup_intent.succeeded',
|
|
7
|
+
};
|
|
8
|
+
export async function stripeWebhookHandler(request, context) {
|
|
9
|
+
try {
|
|
10
|
+
const { modules, services } = context;
|
|
11
|
+
const sig = request.headers.get('stripe-signature');
|
|
12
|
+
if (!sig) {
|
|
13
|
+
logger.error('stripe-signature header was not provided for webhook');
|
|
14
|
+
return new Response(JSON.stringify({
|
|
15
|
+
success: false,
|
|
16
|
+
message: 'stripe-signature header was not provided for webhook',
|
|
17
|
+
name: 'MISSING_SIGNATURE',
|
|
18
|
+
}), {
|
|
19
|
+
status: 400,
|
|
20
|
+
headers: { 'Content-Type': 'application/json' },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (!process.env.STRIPE_ENDPOINT_SECRET) {
|
|
24
|
+
logger.error('env STRIPE_ENDPOINT_SECRET is required for webhook handling');
|
|
25
|
+
return new Response(JSON.stringify({
|
|
26
|
+
success: false,
|
|
27
|
+
message: 'env STRIPE_ENDPOINT_SECRET is required for webhook handling',
|
|
28
|
+
name: 'MISSING_ENDPOINT_SECRET',
|
|
29
|
+
}), {
|
|
30
|
+
status: 500,
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const rawBody = await request.text();
|
|
35
|
+
let event;
|
|
36
|
+
try {
|
|
37
|
+
event = stripe.webhooks.constructEvent(rawBody, sig, process.env.STRIPE_ENDPOINT_SECRET);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
logger.error(`Error constructing event: ${err.message}`);
|
|
41
|
+
return new Response(JSON.stringify({
|
|
42
|
+
success: false,
|
|
43
|
+
message: err.message,
|
|
44
|
+
name: err.name || 'SIGNATURE_VERIFICATION_FAILED',
|
|
45
|
+
}), {
|
|
46
|
+
status: 400,
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (!Object.values(WebhookEventTypes).includes(event.type)) {
|
|
51
|
+
logger.info(`unhandled event type`, {
|
|
52
|
+
type: event.type,
|
|
53
|
+
});
|
|
54
|
+
return new Response(JSON.stringify({
|
|
55
|
+
success: false,
|
|
56
|
+
ignored: true,
|
|
57
|
+
name: 'UNHANDLED_EVENT_TYPE',
|
|
58
|
+
message: `Unhandled event type: ${event.type}. Supported types: ${Object.values(WebhookEventTypes).join(', ')}`,
|
|
59
|
+
}), {
|
|
60
|
+
status: 200,
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const environmentInMetadata = event.data?.object?.metadata?.environment || '';
|
|
65
|
+
const environmentInEnv = process.env.STRIPE_WEBHOOK_ENVIRONMENT || '';
|
|
66
|
+
if (environmentInMetadata !== environmentInEnv) {
|
|
67
|
+
logger.info(`unhandled event environment`, {
|
|
68
|
+
type: event.type,
|
|
69
|
+
environment: environmentInMetadata,
|
|
70
|
+
});
|
|
71
|
+
return new Response(JSON.stringify({
|
|
72
|
+
success: false,
|
|
73
|
+
ignored: true,
|
|
74
|
+
name: 'UNHANDLED_EVENT_ENVIRONMENT',
|
|
75
|
+
message: `Unhandled event environment: ${environmentInMetadata}. Supported environment: ${environmentInEnv}`,
|
|
76
|
+
}), {
|
|
77
|
+
status: 200,
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
logger.info(`Processing event`, {
|
|
82
|
+
type: event.type,
|
|
83
|
+
});
|
|
84
|
+
if (event.type === WebhookEventTypes.PAYMENT_INTENT_SUCCEEDED) {
|
|
85
|
+
const paymentIntent = event.data.object;
|
|
86
|
+
const { orderPaymentId } = paymentIntent.metadata || {};
|
|
87
|
+
logger.info(`checkout with orderPaymentId: ${orderPaymentId}`, {
|
|
88
|
+
type: event.type,
|
|
89
|
+
});
|
|
90
|
+
await modules.orders.payments.logEvent(orderPaymentId, event);
|
|
91
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
92
|
+
orderPaymentId,
|
|
93
|
+
});
|
|
94
|
+
if (!orderPayment) {
|
|
95
|
+
throw new Error(`order payment not found with orderPaymentId: ${orderPaymentId}`);
|
|
96
|
+
}
|
|
97
|
+
const order = await services.orders.checkoutOrder(orderPayment.orderId, {
|
|
98
|
+
paymentContext: {
|
|
99
|
+
paymentIntentId: paymentIntent.id,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (!order)
|
|
103
|
+
throw new Error(`Order with id ${orderPayment.orderId} not found`);
|
|
104
|
+
logger.info(`checkout successful`, {
|
|
105
|
+
orderPaymentId,
|
|
106
|
+
orderId: order._id,
|
|
107
|
+
type: event.type,
|
|
108
|
+
});
|
|
109
|
+
return new Response(JSON.stringify({
|
|
110
|
+
success: true,
|
|
111
|
+
message: 'checkout successful',
|
|
112
|
+
orderId: order._id,
|
|
113
|
+
}), {
|
|
114
|
+
status: 200,
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (event.type === WebhookEventTypes.SETUP_INTENT_SUCCEEDED) {
|
|
119
|
+
const setupIntent = event.data.object;
|
|
120
|
+
const { paymentProviderId, userId } = setupIntent.metadata || {};
|
|
121
|
+
logger.info(`registered payment credential with paymentProviderId: ${paymentProviderId}`, {
|
|
122
|
+
type: event.type,
|
|
123
|
+
userId,
|
|
124
|
+
});
|
|
125
|
+
const paymentCredentials = await services.orders.registerPaymentCredentials(paymentProviderId, {
|
|
126
|
+
transactionContext: {
|
|
127
|
+
setupIntentId: setupIntent.id,
|
|
128
|
+
},
|
|
129
|
+
userId,
|
|
130
|
+
});
|
|
131
|
+
logger.info(`payment credentials registration successful`, {
|
|
132
|
+
userId,
|
|
133
|
+
paymentProviderId,
|
|
134
|
+
paymentCredentialsId: paymentCredentials?._id,
|
|
135
|
+
type: event.type,
|
|
136
|
+
});
|
|
137
|
+
return new Response(JSON.stringify({
|
|
138
|
+
success: true,
|
|
139
|
+
message: 'payment credentials registration successful',
|
|
140
|
+
paymentCredentialsId: paymentCredentials?._id,
|
|
141
|
+
}), {
|
|
142
|
+
status: 200,
|
|
143
|
+
headers: { 'Content-Type': 'application/json' },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return new Response(JSON.stringify({
|
|
147
|
+
success: false,
|
|
148
|
+
message: 'Unhandled event type',
|
|
149
|
+
}), {
|
|
150
|
+
status: 200,
|
|
151
|
+
headers: { 'Content-Type': 'application/json' },
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.error(error);
|
|
156
|
+
return new Response(JSON.stringify({
|
|
157
|
+
success: false,
|
|
158
|
+
message: error.message,
|
|
159
|
+
name: error.name,
|
|
160
|
+
}), {
|
|
161
|
+
status: 500,
|
|
162
|
+
headers: { 'Content-Type': 'application/json' },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -3,17 +3,19 @@ const logger = createLogger('unchained:stripe');
|
|
|
3
3
|
const { STRIPE_SECRET, STRIPE_WEBHOOK_ENVIRONMENT, EMAIL_WEBSITE_NAME } = process.env;
|
|
4
4
|
export let stripe;
|
|
5
5
|
const environment = STRIPE_WEBHOOK_ENVIRONMENT ?? null;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
logger.warn('STRIPE_SECRET is not set, skipping initialization');
|
|
9
|
-
}
|
|
10
|
-
const { default: Stripe } = await import('stripe');
|
|
11
|
-
stripe = new Stripe(STRIPE_SECRET, {
|
|
12
|
-
apiVersion: '2025-12-15.clover',
|
|
13
|
-
});
|
|
6
|
+
if (!STRIPE_SECRET) {
|
|
7
|
+
logger.warn('STRIPE_SECRET is not set, skipping initialization');
|
|
14
8
|
}
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
else {
|
|
10
|
+
try {
|
|
11
|
+
const { default: Stripe } = await import('stripe');
|
|
12
|
+
stripe = new Stripe(STRIPE_SECRET, {
|
|
13
|
+
apiVersion: '2025-12-15.clover',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
logger.warn(`optional peer npm package 'stripe' not installed, stripe adapter will not work`);
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
export const upsertCustomer = async ({ userId, name, email }) => {
|
|
19
21
|
try {
|
package/lib/presets/base.d.ts
CHANGED
|
@@ -20,5 +20,6 @@ import '../worker/http-request.ts';
|
|
|
20
20
|
import '../worker/heartbeat.ts';
|
|
21
21
|
import '../worker/email.ts';
|
|
22
22
|
import '../worker/error-notifications.ts';
|
|
23
|
+
import '../worker/bulk-export.ts';
|
|
23
24
|
import gridfsModules from '../files/gridfs/index.ts';
|
|
24
25
|
export default gridfsModules;
|
package/lib/presets/base.js
CHANGED
|
@@ -20,5 +20,6 @@ import "../worker/http-request.js";
|
|
|
20
20
|
import "../worker/heartbeat.js";
|
|
21
21
|
import "../worker/email.js";
|
|
22
22
|
import "../worker/error-notifications.js";
|
|
23
|
+
import "../worker/bulk-export.js";
|
|
23
24
|
import gridfsModules from "../files/gridfs/index.js";
|
|
24
25
|
export default gridfsModules;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DeliveryPricingRowCategory, DeliveryPricingAdapter, } from '@unchainedshop/core';
|
|
2
|
+
import { resolveTaxCategoryFromDeliveryProvider, SwissTaxCategories } from "../tax/ch.js";
|
|
3
|
+
import isDeliveryAddressInCountry from "../utils/isDeliveryAddressInCountry.js";
|
|
4
|
+
export const DeliverySwissTax = {
|
|
5
|
+
...DeliveryPricingAdapter,
|
|
6
|
+
key: 'shop.unchained.pricing.delivery-swiss-tax',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
label: 'Apply Swiss Tax on Delivery Fees',
|
|
9
|
+
orderIndex: 80,
|
|
10
|
+
isActivatedFor: (context) => {
|
|
11
|
+
if (!context.order)
|
|
12
|
+
return false;
|
|
13
|
+
if (!context.orderDelivery)
|
|
14
|
+
return false;
|
|
15
|
+
return isDeliveryAddressInCountry({
|
|
16
|
+
order: context.order,
|
|
17
|
+
orderDelivery: context.orderDelivery,
|
|
18
|
+
countryCode: context.countryCode,
|
|
19
|
+
}, ['CH', 'LI']);
|
|
20
|
+
},
|
|
21
|
+
actions: (params) => {
|
|
22
|
+
const pricingAdapter = DeliveryPricingAdapter.actions(params);
|
|
23
|
+
const { context } = params;
|
|
24
|
+
return {
|
|
25
|
+
...pricingAdapter,
|
|
26
|
+
calculate: async () => {
|
|
27
|
+
const taxCategory = resolveTaxCategoryFromDeliveryProvider(context.provider) || SwissTaxCategories.DEFAULT;
|
|
28
|
+
const taxRate = taxCategory.rate(context.order?.ordered);
|
|
29
|
+
DeliveryPricingAdapter.log(`DeliverySwissTax -> Tax Multiplicator: ${taxRate}`);
|
|
30
|
+
params.calculationSheet.filterBy({ isTaxable: true }).forEach(({ isNetPrice, ...row }) => {
|
|
31
|
+
if (!isNetPrice) {
|
|
32
|
+
const taxAmount = row.amount - row.amount / (1 + taxRate);
|
|
33
|
+
pricingAdapter.resultSheet().calculation.push({
|
|
34
|
+
...row,
|
|
35
|
+
amount: -taxAmount,
|
|
36
|
+
isTaxable: false,
|
|
37
|
+
isNetPrice: false,
|
|
38
|
+
meta: { adapter: DeliverySwissTax.key },
|
|
39
|
+
});
|
|
40
|
+
pricingAdapter.resultSheet().addTax({
|
|
41
|
+
amount: taxAmount,
|
|
42
|
+
rate: taxRate,
|
|
43
|
+
baseCategory: DeliveryPricingRowCategory.Delivery,
|
|
44
|
+
meta: { adapter: DeliverySwissTax.key },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const taxAmount = row.amount * taxRate;
|
|
49
|
+
pricingAdapter.resultSheet().addTax({
|
|
50
|
+
amount: taxAmount,
|
|
51
|
+
rate: taxRate,
|
|
52
|
+
baseCategory: DeliveryPricingRowCategory.Delivery,
|
|
53
|
+
meta: { adapter: DeliverySwissTax.key },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return pricingAdapter.calculate();
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
export default DeliverySwissTax;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { DeliverySwissTax } from "./adapter.js";
|
|
3
|
+
export const DeliverySwissTaxPlugin = {
|
|
4
|
+
key: 'shop.unchained.pricing.delivery-swiss-tax',
|
|
5
|
+
label: 'Delivery Swiss Tax Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [DeliverySwissTax],
|
|
8
|
+
};
|
|
9
|
+
export default DeliverySwissTaxPlugin;
|
|
10
|
+
export { DeliverySwissTax } from "./adapter.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { OrderDiscountAdapter, } from '@unchainedshop/core';
|
|
2
|
+
export const HundredOff = {
|
|
3
|
+
...OrderDiscountAdapter,
|
|
4
|
+
key: 'shop.unchained.discount.100-off',
|
|
5
|
+
label: '100 Off',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
orderIndex: 10,
|
|
8
|
+
isManualAdditionAllowed: async () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
isManualRemovalAllowed: async () => {
|
|
12
|
+
return true;
|
|
13
|
+
},
|
|
14
|
+
actions: async ({ context }) => ({
|
|
15
|
+
...(await OrderDiscountAdapter.actions({ context })),
|
|
16
|
+
isValidForSystemTriggering: async () => {
|
|
17
|
+
return false;
|
|
18
|
+
},
|
|
19
|
+
isValidForCodeTriggering: async ({ code }) => {
|
|
20
|
+
return code.toUpperCase() === '100OFF';
|
|
21
|
+
},
|
|
22
|
+
discountForPricingAdapterKey: ({ pricingAdapterKey }) => {
|
|
23
|
+
if (pricingAdapterKey === 'shop.unchained.pricing.order-discount') {
|
|
24
|
+
return { fixedRate: 10000 };
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
export default HundredOff;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { HundredOff } from "./adapter.js";
|
|
3
|
+
export const HundredOffPlugin = {
|
|
4
|
+
key: 'shop.unchained.discount.100-off',
|
|
5
|
+
label: '100 Off Discount Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [HundredOff],
|
|
8
|
+
};
|
|
9
|
+
export default HundredOffPlugin;
|
|
10
|
+
export { HundredOff } from "./adapter.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { OrderDiscountAdapter, } from '@unchainedshop/core';
|
|
2
|
+
export const HalfPriceManual = {
|
|
3
|
+
...OrderDiscountAdapter,
|
|
4
|
+
key: 'shop.unchained.discount.half-price-manual',
|
|
5
|
+
label: 'Half Price Manual',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
orderIndex: 10,
|
|
8
|
+
isManualAdditionAllowed: async () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
isManualRemovalAllowed: async () => {
|
|
12
|
+
return true;
|
|
13
|
+
},
|
|
14
|
+
actions: async ({ context }) => ({
|
|
15
|
+
...(await OrderDiscountAdapter.actions({ context })),
|
|
16
|
+
isValidForSystemTriggering: async () => {
|
|
17
|
+
return false;
|
|
18
|
+
},
|
|
19
|
+
isValidForCodeTriggering: async ({ code }) => {
|
|
20
|
+
return code === 'HALFPRICE';
|
|
21
|
+
},
|
|
22
|
+
discountForPricingAdapterKey({ pricingAdapterKey }) {
|
|
23
|
+
if (pricingAdapterKey === 'shop.unchained.pricing.product-discount') {
|
|
24
|
+
return { rate: 0.5 };
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
export default HalfPriceManual;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { HalfPriceManual } from "./adapter.js";
|
|
3
|
+
export const HalfPriceManualPlugin = {
|
|
4
|
+
key: 'shop.unchained.discount.half-price-manual',
|
|
5
|
+
label: 'Half Price Manual Discount Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [HalfPriceManual],
|
|
8
|
+
};
|
|
9
|
+
export default HalfPriceManualPlugin;
|
|
10
|
+
export { HalfPriceManual } from "./adapter.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DeliveryPricingAdapter } from '@unchainedshop/core';
|
|
2
|
+
export const DeliveryFreePrice = {
|
|
3
|
+
...DeliveryPricingAdapter,
|
|
4
|
+
key: 'shop.unchained.pricing.delivery-free',
|
|
5
|
+
label: 'Free Delivery',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
orderIndex: 0,
|
|
8
|
+
isActivatedFor: () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
actions: (params) => {
|
|
12
|
+
const pricingAdapter = DeliveryPricingAdapter.actions(params);
|
|
13
|
+
return {
|
|
14
|
+
...pricingAdapter,
|
|
15
|
+
calculate: async () => {
|
|
16
|
+
pricingAdapter.resultSheet().addFee({
|
|
17
|
+
amount: 0,
|
|
18
|
+
isTaxable: false,
|
|
19
|
+
isNetPrice: false,
|
|
20
|
+
meta: { adapter: DeliveryFreePrice.key },
|
|
21
|
+
});
|
|
22
|
+
return pricingAdapter.calculate();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default DeliveryFreePrice;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { DeliveryFreePrice } from "./adapter.js";
|
|
3
|
+
export const DeliveryFreePricePlugin = {
|
|
4
|
+
key: 'shop.unchained.pricing.delivery-free',
|
|
5
|
+
label: 'Free Delivery Pricing Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [DeliveryFreePrice],
|
|
8
|
+
};
|
|
9
|
+
export default DeliveryFreePricePlugin;
|
|
10
|
+
export { DeliveryFreePrice } from "./adapter.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PaymentPricingAdapter } from '@unchainedshop/core';
|
|
2
|
+
export const PaymentFreePrice = {
|
|
3
|
+
...PaymentPricingAdapter,
|
|
4
|
+
key: 'shop.unchained.pricing.payment-free',
|
|
5
|
+
label: 'Free Payment',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
orderIndex: 0,
|
|
8
|
+
isActivatedFor: () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
actions: (params) => {
|
|
12
|
+
const pricingAdapter = PaymentPricingAdapter.actions(params);
|
|
13
|
+
return {
|
|
14
|
+
...pricingAdapter,
|
|
15
|
+
calculate: async () => {
|
|
16
|
+
pricingAdapter.resultSheet().addFee({
|
|
17
|
+
amount: 0,
|
|
18
|
+
isNetPrice: false,
|
|
19
|
+
isTaxable: false,
|
|
20
|
+
meta: { adapter: PaymentFreePrice.key },
|
|
21
|
+
});
|
|
22
|
+
return pricingAdapter.calculate();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default PaymentFreePrice;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {} from '@unchainedshop/core';
|
|
2
|
+
import { PaymentFreePrice } from "./adapter.js";
|
|
3
|
+
export const PaymentFreePricePlugin = {
|
|
4
|
+
key: 'shop.unchained.pricing.payment-free',
|
|
5
|
+
label: 'Free Payment Pricing Plugin',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
adapters: [PaymentFreePrice],
|
|
8
|
+
};
|
|
9
|
+
export default PaymentFreePricePlugin;
|
|
10
|
+
export { PaymentFreePrice } from "./adapter.js";
|