@unchainedshop/plugins 4.8.4 → 4.8.9

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.
@@ -19,8 +19,7 @@ export async function minioWebhookHandler(request, context) {
19
19
  headers: { 'Content-Type': 'application/json' },
20
20
  });
21
21
  }
22
- const body = await request.json();
23
- const { Records = [], EventName } = body;
22
+ const { Records = [], EventName } = (await request.json());
24
23
  if (EventName === 's3:ObjectCreated:Put') {
25
24
  const [{ s3 }] = Records;
26
25
  const { object } = s3;
@@ -1,6 +1,8 @@
1
1
  import { type UnchainedCore } from '@unchainedshop/core';
2
+ import type { RolesInterface } from '@unchainedshop/roles';
2
3
  export declare function tempUploadHandler(request: Request, context: UnchainedCore & {
3
4
  params: Record<string, string>;
5
+ roles?: RolesInterface;
4
6
  userId?: string;
5
7
  user?: any;
6
8
  }): Promise<Response>;
@@ -1,10 +1,9 @@
1
1
  import { createLogger } from '@unchainedshop/logger';
2
2
  import { getFileAdapter } from '@unchainedshop/core';
3
- import { Roles } from '@unchainedshop/roles';
4
3
  const logger = createLogger('unchained:temp-upload');
5
4
  export async function tempUploadHandler(request, context) {
6
5
  try {
7
- const hasPermission = await Roles.userHasPermission(context, 'uploadTempFile', []);
6
+ const hasPermission = await context.roles?.userHasPermission(context, 'uploadTempFile', []);
8
7
  if (!hasPermission) {
9
8
  return Response.json({ error: 'Permission denied', message: 'User does not have uploadTempFile permission' }, { status: 403 });
10
9
  }
@@ -13,7 +13,7 @@ const AppleNotificationTypes = {
13
13
  export async function appleIAPWebhookHandler(request, context) {
14
14
  try {
15
15
  const { modules, services } = context;
16
- const responseBody = await request.json();
16
+ const responseBody = (await request.json());
17
17
  if (responseBody.password !== APPLE_IAP_SHARED_SECRET) {
18
18
  logger.warn('Apple IAP Webhook: Invalid shared secret');
19
19
  return new Response(JSON.stringify({
@@ -3,7 +3,7 @@ import handleWebhook from "./handle-webhook.js";
3
3
  const logger = createLogger('unchained:cryptopay');
4
4
  export async function cryptopayWebhookHandler(request, context) {
5
5
  try {
6
- const body = await request.json();
6
+ const body = (await request.json());
7
7
  await handleWebhook(body, context);
8
8
  return new Response(JSON.stringify({ success: true }), {
9
9
  status: 200,
@@ -1,13 +1,14 @@
1
1
  import { createLogger } from '@unchainedshop/logger';
2
2
  import {} from '@unchainedshop/api';
3
3
  import { OrderStatus } from '@unchainedshop/core-orders';
4
+ import { timingSafeStringEqual } from '@unchainedshop/utils';
4
5
  import {} from "./module.js";
5
6
  import {} from '@unchainedshop/core-products';
6
7
  const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
7
8
  const logger = createLogger('unchained:cryptopay:handler');
8
9
  export default async function handleWebhook({ secret, wallet, price, ping, }, context) {
9
10
  const { modules, services } = context;
10
- if (secret !== CRYPTOPAY_SECRET) {
11
+ if (!CRYPTOPAY_SECRET || !(await timingSafeStringEqual(secret, CRYPTOPAY_SECRET))) {
11
12
  logger.warn(`webhook called with invalid secret`);
12
13
  throw new Error('Secret invalid');
13
14
  }
@@ -26,10 +26,13 @@ const getMarketplaceSplits = async ({ order, orderPayment, config, }) => {
26
26
  currencyCode: order.currencyCode,
27
27
  });
28
28
  const { amount: total } = pricing.total({ useNetPrice: false });
29
- return Promise.all(config
29
+ const roundedTotal = Math.round(total);
30
+ const splits = config
30
31
  .filter((item) => item.key === 'marketplaceSplit')
31
32
  .map((item) => {
32
- const [subMerchantId, staticDiscountId, sharePercentage] = item.value || ''.split(';').map((f) => f.trim());
33
+ const [subMerchantId, staticDiscountId, sharePercentage] = (item.value || '')
34
+ .split(';')
35
+ .map((f) => f.trim());
33
36
  const { amount: discountSum } = pricingForOrderPayment.total({
34
37
  category: PaymentPricingRowCategory.Discount,
35
38
  discountId: staticDiscountId,
@@ -42,7 +45,12 @@ const getMarketplaceSplits = async ({ order, orderPayment, config, }) => {
42
45
  amount,
43
46
  commission,
44
47
  };
45
- }));
48
+ });
49
+ if (splits.length > 0) {
50
+ const splitsSum = splits.reduce((sum, s) => sum + s.amount, 0);
51
+ splits[splits.length - 1].amount += roundedTotal - splitsSum;
52
+ }
53
+ return splits;
46
54
  };
47
55
  export const Datatrans = {
48
56
  ...PaymentAdapter,
@@ -3,7 +3,7 @@ const logger = createLogger('unchained:payrexx');
3
3
  export async function payrexxWebhookHandler(request, context) {
4
4
  try {
5
5
  const { modules, services } = context;
6
- const body = await request.json();
6
+ const body = (await request.json());
7
7
  const { transaction } = body;
8
8
  if (!transaction) {
9
9
  logger.info('Unhandled event type', {
@@ -1,9 +1,9 @@
1
1
  const getRedirects = () => {
2
- const { EMAIL_WEBSITE_URL, DATATRANS_SUCCESS_PATH = '/payrexx/success', DATATRANS_ERROR_PATH = '/payrexx/error', DATATRANS_CANCEL_PATH = '/payrexx/cancel', } = process.env;
2
+ const { EMAIL_WEBSITE_URL, PAYREXX_SUCCESS_PATH = '/payrexx/success', PAYREXX_ERROR_PATH = '/payrexx/error', PAYREXX_CANCEL_PATH = '/payrexx/cancel', } = process.env;
3
3
  return {
4
- successRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_SUCCESS_PATH}`,
5
- failedRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_ERROR_PATH}`,
6
- cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_CANCEL_PATH}`,
4
+ successRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_SUCCESS_PATH}`,
5
+ failedRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_ERROR_PATH}`,
6
+ cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_CANCEL_PATH}`,
7
7
  };
8
8
  };
9
9
  export const mapOrderDataToGatewayObject = ({ order, orderPayment, pricing }, options = {}) => {
@@ -10,7 +10,7 @@ else {
10
10
  try {
11
11
  const { default: Stripe } = await import('stripe');
12
12
  stripe = new Stripe(STRIPE_SECRET, {
13
- apiVersion: '2026-02-25.clover',
13
+ apiVersion: '2026-03-25.dahlia',
14
14
  });
15
15
  }
16
16
  catch {
@@ -1,7 +1,9 @@
1
1
  import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { RolesInterface } from '@unchainedshop/roles';
2
3
  export declare function bulkImportHandler(request: Request, context: UnchainedCore & {
3
4
  params: Record<string, string>;
4
5
  rawRequest?: any;
6
+ roles?: RolesInterface;
5
7
  userId?: string;
6
8
  user?: any;
7
9
  }): Promise<Response>;
@@ -1,9 +1,8 @@
1
1
  import { createLogger } from '@unchainedshop/logger';
2
- import { Roles } from '@unchainedshop/roles';
3
2
  const logger = createLogger('unchained:bulk-import');
4
3
  export async function bulkImportHandler(request, context) {
5
4
  try {
6
- const hasPermission = await Roles.userHasPermission(context, 'bulkImport', []);
5
+ const hasPermission = await context.roles?.userHasPermission(context, 'bulkImport', []);
7
6
  if (!hasPermission) {
8
7
  return Response.json({ error: 'Permission denied', message: 'User does not have bulkImport permission' }, { status: 403 });
9
8
  }
@@ -31,7 +31,7 @@ export const BulkGateWorker = {
31
31
  },
32
32
  body: JSON.stringify(requestBody),
33
33
  });
34
- const responseData = await response.json();
34
+ const responseData = (await response.json());
35
35
  if (responseData.error || responseData.type) {
36
36
  return {
37
37
  success: false,
@@ -26,7 +26,7 @@ export const SmsWorkerPlugin = {
26
26
  ...params,
27
27
  }),
28
28
  });
29
- const data = await response.json();
29
+ const data = (await response.json());
30
30
  if (!response.ok) {
31
31
  return {
32
32
  success: false,
@@ -1,11 +1,9 @@
1
1
  import { WorkerAdapter, WorkerDirector, schedule } from '@unchainedshop/core';
2
2
  import { resolveBestCurrency } from '@unchainedshop/utils';
3
3
  const getExchangeRates = async (base) => {
4
- return fetch(`https://api.coinbase.com/v2/exchange-rates?currency=${base}`, {
5
- method: 'GET',
6
- })
7
- .then((res) => res.json())
8
- .then((r) => r?.data);
4
+ const res = await fetch(`https://api.coinbase.com/v2/exchange-rates?currency=${base}`);
5
+ const { data } = (await res.json());
6
+ return data;
9
7
  };
10
8
  const everyMinute = schedule.parse.cron('* * * * *');
11
9
  export const UpdateCoinbaseRates = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/plugins",
3
3
  "description": "Official plugin collection for the Unchained Engine with payment, delivery, and pricing adapters",
4
- "version": "4.8.4",
4
+ "version": "4.8.9",
5
5
  "main": "lib/plugins-index.js",
6
6
  "types": "lib/plugins-index.d.ts",
7
7
  "exports": {
@@ -63,7 +63,7 @@
63
63
  "minio": "8.x",
64
64
  "nodemailer": ">= 6.9 < 9",
65
65
  "p-memoize": "8.x",
66
- "stripe": ">= 19 < 21",
66
+ "stripe": ">= 19 < 22",
67
67
  "web-push": ">= 3.6 <4",
68
68
  "xml-js": ">= 1.6 < 2"
69
69
  },
@@ -124,7 +124,7 @@
124
124
  "minio": "^8.0.5",
125
125
  "nodemailer": "^8.0.2",
126
126
  "p-memoize": "^8.0.0",
127
- "stripe": "^20.0.0",
127
+ "stripe": "^21.0.1",
128
128
  "typescript": "^5.8.3",
129
129
  "web-push": "^3.6.7",
130
130
  "xml-js": "^1.6.11"