payment-kit 1.20.14 → 1.20.16

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.
@@ -1,10 +1,19 @@
1
- import { BN } from '@ocap/util';
1
+ import { BN, fromUnitToToken } from '@ocap/util';
2
2
 
3
3
  import pick from 'lodash/pick';
4
- import { Coupon, Customer, Discount, PromotionCode, Subscription } from '../../store/models';
5
- import type { CheckoutSession, TLineItemExpanded } from '../../store/models';
4
+ import {
5
+ Coupon,
6
+ Customer,
7
+ Discount,
8
+ PromotionCode,
9
+ Subscription,
10
+ CheckoutSession,
11
+ PaymentCurrency,
12
+ } from '../../store/models';
13
+ import type { TLineItemExpanded } from '../../store/models';
6
14
  import logger from '../logger';
7
15
  import { emitAsync } from '../event';
16
+ import { formatNumber } from '../util';
8
17
 
9
18
  export function validCoupon(coupon: Coupon, lineItems?: TLineItemExpanded[]) {
10
19
  if (!coupon.valid) {
@@ -75,13 +84,20 @@ export async function validPromotionCode(
75
84
  ? promotionCode.restrictions?.minimum_amount
76
85
  : promotionCode.restrictions?.currency_options?.[currencyId]?.minimum_amount;
77
86
 
87
+ const currency = await PaymentCurrency.findByPk(currencyId);
78
88
  if (minimumAmount) {
79
89
  const amountBN = new BN(amount);
80
90
  const minimumBN = new BN(minimumAmount);
81
91
  if (amountBN.lt(minimumBN)) {
92
+ if (!currency) {
93
+ return {
94
+ valid: false,
95
+ reason: 'This promotion requires a minimum purchase amount. Please add more items to your cart.',
96
+ };
97
+ }
82
98
  return {
83
99
  valid: false,
84
- reason: 'This promotion requires a minimum purchase amount. Please add more items to your cart.',
100
+ reason: `This promotion requires a minimum purchase amount of ${formatNumber(fromUnitToToken(minimumBN, currency?.decimal || 2))} ${currency?.symbol}. Please add more items to your cart.`,
85
101
  };
86
102
  }
87
103
  }
@@ -296,7 +312,7 @@ async function processSubscriptionDiscount({
296
312
  baseDiscountData: any;
297
313
  existingDiscountMap: Map<string, any>;
298
314
  checkoutSessionId: string;
299
- }): Promise<{ discountRecord: any; shouldUpdateUsage: boolean }> {
315
+ }): Promise<{ discountRecord: Discount; shouldUpdateUsage: boolean }> {
300
316
  try {
301
317
  const existingDiscount = existingDiscountMap.get(subscriptionId);
302
318
 
@@ -342,6 +358,7 @@ async function processSubscriptionDiscount({
342
358
  const newDiscount = await Discount.create({
343
359
  ...baseDiscountData,
344
360
  subscription_id: subscriptionId,
361
+ confirmed: false,
345
362
  metadata: {
346
363
  ...baseDiscountData.metadata,
347
364
  subscription_id: subscriptionId,
@@ -375,7 +392,7 @@ async function processNonSubscriptionDiscount(
375
392
  baseDiscountData: any,
376
393
  existingDiscountMap: Map<string, any>,
377
394
  checkoutSessionId: string
378
- ): Promise<{ discountRecord: any; shouldUpdateUsage: boolean }> {
395
+ ): Promise<{ discountRecord: Discount; shouldUpdateUsage: boolean }> {
379
396
  try {
380
397
  const existingDiscount = existingDiscountMap.get('no_subscription');
381
398
 
@@ -412,7 +429,10 @@ async function processNonSubscriptionDiscount(
412
429
  }
413
430
 
414
431
  // Create new discount record
415
- const newDiscount = await Discount.create(baseDiscountData);
432
+ const newDiscount = await Discount.create({
433
+ ...baseDiscountData,
434
+ confirmed: false,
435
+ });
416
436
 
417
437
  // Lock coupon and promotion code when discount is created
418
438
  await lockDiscountResources(baseDiscountData.coupon_id, baseDiscountData.promotion_code_id);
@@ -710,6 +730,13 @@ export async function createDiscountRecordsForCheckout({
710
730
  updatedPromotionCodes,
711
731
  checkoutSessionId: checkoutSession.id,
712
732
  });
733
+ await Promise.all(
734
+ discountRecords.map(async (discountRecord) => {
735
+ await discountRecord.update({
736
+ confirmed: true,
737
+ });
738
+ })
739
+ );
713
740
  } catch (error) {
714
741
  logger.error('Failed to update usage counts, but continuing', {
715
742
  couponId: coupon.id,
@@ -1,8 +1,9 @@
1
1
  import { BN } from '@ocap/util';
2
- import { Coupon, Discount, PaymentCurrency, PromotionCode } from '../../store/models';
2
+ import { Coupon, Discount, PaymentCurrency, PromotionCode, CheckoutSession } from '../../store/models';
3
3
  import type { TLineItemExpanded } from '../../store/models';
4
4
  import { getPriceUintAmountByCurrency } from '../price';
5
5
  import { validCoupon, checkPromotionCodeEligibility, calculateDiscountAmount } from './coupon';
6
+ import { emitAsync } from '../event';
6
7
  import logger from '../logger';
7
8
 
8
9
  const getItemsTotalAmount = (lineItems: TLineItemExpanded[], currencyId: string, options?: { trialing?: boolean }) => {
@@ -347,3 +348,84 @@ export async function applySubscriptionDiscount({
347
348
 
348
349
  return result;
349
350
  }
351
+
352
+ async function rollbackUsageForResource<T extends Coupon | PromotionCode>(
353
+ resource: T,
354
+ resourceType: 'coupon' | 'promotion-code'
355
+ ): Promise<void> {
356
+ const currentUsage = resource.times_redeemed || 0;
357
+ if (currentUsage <= 0) return;
358
+
359
+ if (resourceType === 'coupon') {
360
+ await (resource as Coupon).update({
361
+ times_redeemed: currentUsage - 1,
362
+ valid: true,
363
+ });
364
+ } else {
365
+ await (resource as PromotionCode).update({
366
+ times_redeemed: currentUsage - 1,
367
+ active: true,
368
+ });
369
+ }
370
+
371
+ logger.info('Updated usage for resource', {
372
+ resourceId: resource.id,
373
+ resourceType,
374
+ currentUsage: currentUsage - 1,
375
+ });
376
+
377
+ emitAsync('discount-status.queued', resource, resourceType, true);
378
+ }
379
+
380
+ export async function rollbackDiscountUsageForCheckoutSession(checkoutSessionId: string): Promise<void> {
381
+ try {
382
+ const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
383
+ if (!checkoutSession) {
384
+ logger.error('Checkout session not found', { checkoutSessionId });
385
+ return;
386
+ }
387
+
388
+ if (checkoutSession.status === 'complete') {
389
+ logger.error('Cannot rollback completed session', { checkoutSessionId });
390
+ return;
391
+ }
392
+
393
+ const discounts = (await Discount.findAll({
394
+ where: { checkout_session_id: checkoutSessionId },
395
+ include: [
396
+ { model: Coupon, as: 'coupon' },
397
+ { model: PromotionCode, as: 'promotionCode', required: false },
398
+ ],
399
+ })) as (Discount & { coupon: Coupon; promotionCode?: PromotionCode })[];
400
+
401
+ if (discounts.length === 0) {
402
+ logger.info('No discounts found for checkout session', { checkoutSessionId });
403
+ return;
404
+ }
405
+
406
+ const uniqueCoupons = new Map<string, Coupon>();
407
+ const uniquePromotionCodes = new Map<string, PromotionCode>();
408
+
409
+ discounts.forEach((discount) => {
410
+ if (discount.confirmed) {
411
+ if (discount.coupon?.id && !uniqueCoupons.has(discount.coupon.id)) {
412
+ uniqueCoupons.set(discount.coupon.id, discount.coupon);
413
+ }
414
+ if (discount.promotionCode?.id && !uniquePromotionCodes.has(discount.promotionCode.id)) {
415
+ uniquePromotionCodes.set(discount.promotionCode.id, discount.promotionCode);
416
+ }
417
+ }
418
+ });
419
+
420
+ const rollbackPromises = [
421
+ ...Array.from(uniquePromotionCodes.values()).map((pc) => rollbackUsageForResource(pc, 'promotion-code')),
422
+ ...Array.from(uniqueCoupons.values()).map((c) => rollbackUsageForResource(c, 'coupon')),
423
+ ];
424
+
425
+ await Promise.all(rollbackPromises);
426
+
427
+ await Promise.all(discounts.map((discount) => discount.destroy()));
428
+ } catch (error) {
429
+ logger.error('Discount rollback failed', { checkoutSessionId, error: error.message });
430
+ }
431
+ }
@@ -21,6 +21,7 @@ export const vendorTimeoutMinutes: number = process.env.VENDOR_TIMEOUT_MINUTES
21
21
  : 10; // 默认 10 分钟超时
22
22
 
23
23
  export const shortUrlApiKey: string = process.env.SHORT_URL_API_KEY || '';
24
+ export const shortUrlDomain: string = process.env.SHORT_URL_DOMAIN || 's.abtnet.io';
24
25
 
25
26
  // sequelize 配置相关
26
27
  export const sequelizeOptionsPoolMin: number = process.env.SEQUELIZE_OPTIONS_POOL_MIN
@@ -368,7 +368,7 @@ export function isCreditSufficientForPayment(args: {
368
368
  const balance = tokens[paymentCurrency.id] || '0';
369
369
 
370
370
  if (amount === '0') {
371
- return { sufficient: false, balance, reason: 'NO_REQUIREMENT' };
371
+ return { sufficient: true, balance };
372
372
  }
373
373
 
374
374
  if (new BN(balance).lt(new BN(amount))) {
@@ -1,5 +1,5 @@
1
1
  import logger from './logger';
2
- import { shortUrlApiKey } from './env';
2
+ import { shortUrlApiKey, shortUrlDomain } from './env';
3
3
 
4
4
  interface ShortUrlResponse {
5
5
  shortUrl: string;
@@ -43,14 +43,14 @@ export async function formatToShortUrl({
43
43
  maxVisits,
44
44
  tags: [],
45
45
  shortCodeLength: 8,
46
- domain: 's.abtnet.io',
46
+ domain: shortUrlDomain,
47
47
  findIfExists: true,
48
48
  validateUrl: true,
49
49
  forwardQuery: true,
50
50
  crawlable: true,
51
51
  };
52
52
 
53
- const response = await fetch('https://s.abtnet.io/rest/v3/short-urls', {
53
+ const response = await fetch(`https://${shortUrlDomain}/rest/v3/short-urls`, {
54
54
  method: 'POST',
55
55
  headers: {
56
56
  'Content-Type': 'application/json',
@@ -89,7 +89,7 @@ export class LauncherAdapter implements VendorAdapter {
89
89
  amount: params.amount,
90
90
  currency: params.currency,
91
91
  quantity: params.quantity,
92
- paymentIntentId: params.paymentIntentId,
92
+ invoiceId: params.invoiceId,
93
93
  customParams: params.customParams,
94
94
  },
95
95
  installationInfo: {
@@ -14,7 +14,7 @@ export interface FulfillOrderParams {
14
14
  productCode: string;
15
15
  customerId: string;
16
16
  quantity: number;
17
- paymentIntentId: string;
17
+ invoiceId: string;
18
18
  amount: string;
19
19
  currency: string;
20
20
 
@@ -38,7 +38,7 @@ export interface OrderDetails {
38
38
  amount: string;
39
39
  currency: string;
40
40
  quantity: number;
41
- paymentIntentId: string;
41
+ invoiceId: string;
42
42
  customParams?: Record<string, any>;
43
43
  }
44
44
 
@@ -66,7 +66,6 @@ export interface ReturnRequestParams {
66
66
  orderId: string;
67
67
  vendorOrderId?: string;
68
68
  reason: string;
69
- paymentIntentId: string;
70
69
  customParams?: Record<string, any>;
71
70
  }
72
71
 
@@ -43,7 +43,7 @@ export class VendorFulfillmentService {
43
43
  checkoutSessionId: string;
44
44
  amount_total: string;
45
45
  customer_id: string;
46
- payment_intent_id: string | null;
46
+ invoiceId: string;
47
47
  currency_id: string;
48
48
  customer_did: string;
49
49
  },
@@ -75,7 +75,7 @@ export class VendorFulfillmentService {
75
75
  productCode: vendorConfig.vendor_id,
76
76
  customerId: orderInfo.customer_id || '',
77
77
  quantity: 1,
78
- paymentIntentId: orderInfo.payment_intent_id || '',
78
+ invoiceId: orderInfo.invoiceId,
79
79
  amount: orderInfo.amount_total,
80
80
  currency: orderInfo.currency_id,
81
81
 
@@ -124,34 +124,18 @@ export class VendorFulfillmentService {
124
124
  }
125
125
  }
126
126
 
127
- static async createVendorPayouts(
128
- checkoutSessionId: string,
129
- fulfillmentResults?: VendorFulfillmentResult[]
130
- ): Promise<void> {
127
+ static async createVendorPayouts(checkoutSession: CheckoutSession, paymentIntent: PaymentIntent): Promise<void> {
131
128
  try {
132
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
133
- if (!checkoutSession) {
134
- throw new Error(`CheckoutSession not found: ${checkoutSessionId}`);
135
- }
136
-
137
- let paymentMethodId = '';
138
- if (checkoutSession.payment_intent_id) {
139
- const paymentIntent = await PaymentIntent.findByPk(checkoutSession.payment_intent_id);
140
- if (paymentIntent) {
141
- paymentMethodId = paymentIntent.payment_method_id;
142
- }
143
- }
129
+ const paymentMethodId = paymentIntent.payment_method_id;
130
+ const paymentIntentId = paymentIntent.id;
144
131
 
145
132
  // If fulfillmentResults not provided, calculate commission info
146
- let commissionData = fulfillmentResults;
147
- if (!commissionData) {
148
- commissionData = checkoutSession.vendor_info?.map((vendorInfo: any) => ({
149
- vendorId: vendorInfo.vendor_id,
150
- orderId: vendorInfo.order_id,
151
- status: vendorInfo.status,
152
- commissionAmount: vendorInfo.commissionAmount,
153
- })) as VendorFulfillmentResult[];
154
- }
133
+ const commissionData = checkoutSession.vendor_info?.map((vendorInfo: any) => ({
134
+ vendorId: vendorInfo.vendor_id,
135
+ orderId: vendorInfo.order_id,
136
+ status: vendorInfo.status,
137
+ commissionAmount: vendorInfo.commissionAmount,
138
+ })) as VendorFulfillmentResult[];
155
139
 
156
140
  const payoutPromises = commissionData
157
141
  .filter((result) => result.status !== 'failed' && new BN(result.commissionAmount).gt(new BN('0')))
@@ -170,7 +154,7 @@ export class VendorFulfillmentService {
170
154
  amount: result.commissionAmount,
171
155
  currency_id: checkoutSession.currency_id,
172
156
  customer_id: checkoutSession.customer_id || '',
173
- payment_intent_id: checkoutSession.payment_intent_id || '',
157
+ payment_intent_id: paymentIntentId,
174
158
  payment_method_id: paymentMethodId,
175
159
  status: paymentMethod?.type === 'stripe' ? 'deferred' : 'pending',
176
160
  attempt_count: 0,
@@ -187,12 +171,14 @@ export class VendorFulfillmentService {
187
171
  await Promise.all(payoutPromises);
188
172
 
189
173
  logger.info('Vendor payouts created', {
190
- checkoutSessionId,
174
+ checkoutSessionId: checkoutSession.id,
175
+ paymentIntentId: paymentIntent.id,
191
176
  payoutCount: commissionData.filter((r) => r.status !== 'failed').length,
192
177
  });
193
178
  } catch (error: any) {
194
179
  logger.error('Failed to create vendor payouts', {
195
- checkoutSessionId,
180
+ checkoutSessionId: checkoutSession.id,
181
+ paymentIntentId: paymentIntent.id,
196
182
  error: error.message,
197
183
  });
198
184
  throw error;
@@ -21,6 +21,7 @@ import {
21
21
  SubscriptionItem,
22
22
  } from '../store/models';
23
23
  import { getCheckoutSessionSubscriptionIds } from '../libs/session';
24
+ import { rollbackDiscountUsageForCheckoutSession } from '../libs/discount/discount';
24
25
 
25
26
  type CheckoutSessionJob = {
26
27
  id: string;
@@ -240,6 +241,8 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
240
241
  );
241
242
  }
242
243
 
244
+ await rollbackDiscountUsageForCheckoutSession(checkoutSession.id);
245
+
243
246
  // update price lock status
244
247
  for (const item of checkoutSession.line_items) {
245
248
  const price = await Price.findByPk(item.price_id);
@@ -914,6 +914,11 @@ export const handlePayment = async (job: PaymentJob) => {
914
914
  return;
915
915
  }
916
916
 
917
+ if (paymentIntent.amount === '0') {
918
+ await handlePaymentSucceed(paymentIntent);
919
+ return;
920
+ }
921
+
917
922
  await paymentIntent.update({ status: 'processing', last_payment_error: null });
918
923
 
919
924
  if (paymentMethod.type === 'arcblock') {
@@ -1,29 +1,26 @@
1
1
  import { events } from '../../libs/event';
2
2
  import logger from '../../libs/logger';
3
3
  import createQueue from '../../libs/queue';
4
+ import { Invoice } from '../../store/models';
4
5
  import { CheckoutSession } from '../../store/models/checkout-session';
5
6
  import { PaymentIntent } from '../../store/models/payment-intent';
6
- import { Product } from '../../store/models/product';
7
7
  import { Price } from '../../store/models/price';
8
+ import { Product } from '../../store/models/product';
8
9
  import { depositVaultQueue } from '../payment';
9
10
  import { startVendorFulfillment, triggerCommissionProcess, triggerCoordinatorCheck } from './fulfillment-coordinator';
10
11
 
11
12
  type VendorCommissionJob = {
12
- paymentIntentId: string;
13
+ invoiceId: string;
13
14
  retryOnError?: boolean;
14
15
  };
15
16
 
16
- async function checkIfPaymentIntentHasVendors(
17
- paymentIntent: PaymentIntent,
18
- checkoutSession: CheckoutSession
19
- ): Promise<boolean> {
17
+ async function checkIfPaymentIntentHasVendors(checkoutSession: CheckoutSession): Promise<boolean> {
20
18
  try {
21
19
  // Extract price_ids from line_items, then find corresponding product_ids
22
20
  const priceIds = checkoutSession.line_items.map((item: any) => item.price_id).filter(Boolean);
23
21
 
24
22
  if (priceIds.length === 0) {
25
23
  logger.warn('No price IDs found in checkout session line items', {
26
- paymentIntentId: paymentIntent.id,
27
24
  checkoutSessionId: checkoutSession.id,
28
25
  });
29
26
  return false;
@@ -39,7 +36,6 @@ async function checkIfPaymentIntentHasVendors(
39
36
 
40
37
  if (productIds.length === 0) {
41
38
  logger.warn('No product IDs found from prices', {
42
- paymentIntentId: paymentIntent.id,
43
39
  checkoutSessionId: checkoutSession.id,
44
40
  priceIds,
45
41
  });
@@ -57,28 +53,28 @@ async function checkIfPaymentIntentHasVendors(
57
53
  return hasVendorConfig;
58
54
  } catch (error: any) {
59
55
  logger.error('Failed to check vendor configuration', {
60
- paymentIntentId: paymentIntent.id,
61
56
  error,
62
57
  });
63
58
  return false;
64
59
  }
65
60
  }
66
61
 
67
- async function executeDirectDepositVault(paymentIntent: PaymentIntent): Promise<void> {
68
- const exist = await depositVaultQueue.get(`deposit-vault-${paymentIntent.currency_id}`);
62
+ async function executeDirectDepositVault(invoice: Invoice): Promise<void> {
63
+ const currencyId = invoice.currency_id;
64
+ const exist = await depositVaultQueue.get(`deposit-vault-${currencyId}`);
69
65
  if (!exist) {
70
66
  depositVaultQueue.push({
71
- id: `deposit-vault-${paymentIntent.currency_id}`,
72
- job: { currencyId: paymentIntent.currency_id },
67
+ id: `deposit-vault-${currencyId}`,
68
+ job: { currencyId },
73
69
  });
74
70
  logger.info('Deposit vault job queued', {
75
- paymentIntentId: paymentIntent.id,
76
- currencyId: paymentIntent.currency_id,
71
+ paymentIntentId: invoice.payment_intent_id,
72
+ currencyId,
77
73
  });
78
74
  } else {
79
75
  logger.info('Deposit vault job already exists', {
80
- paymentIntentId: paymentIntent.id,
81
- currencyId: paymentIntent.currency_id,
76
+ paymentIntentId: invoice.payment_intent_id,
77
+ currencyId,
82
78
  });
83
79
  }
84
80
  }
@@ -88,53 +84,53 @@ export const handleVendorCommission = async (job: VendorCommissionJob) => {
88
84
 
89
85
  let checkoutSession: CheckoutSession | null = null;
90
86
  try {
91
- const paymentIntent = await PaymentIntent.findByPk(job.paymentIntentId);
92
- if (!paymentIntent) {
93
- logger.warn('PaymentIntent not found', { id: job.paymentIntentId });
87
+ const invoice = await Invoice.findByPk(job.invoiceId);
88
+ if (!invoice) {
89
+ logger.warn('invoice not found', { id: job.invoiceId });
94
90
  return;
95
91
  }
96
92
 
97
93
  // Find CheckoutSession through PaymentIntent
98
- checkoutSession = await CheckoutSession.findByPaymentIntentId(paymentIntent.id);
94
+ checkoutSession = await CheckoutSession.findByInvoiceId(invoice.id);
99
95
  if (!checkoutSession) {
100
- await executeDirectDepositVault(paymentIntent);
96
+ await executeDirectDepositVault(invoice);
101
97
  return;
102
98
  }
103
99
 
104
- const hasVendorConfig = await checkIfPaymentIntentHasVendors(paymentIntent, checkoutSession);
100
+ const hasVendorConfig = await checkIfPaymentIntentHasVendors(checkoutSession);
105
101
  if (!hasVendorConfig) {
106
- await executeDirectDepositVault(paymentIntent);
102
+ await executeDirectDepositVault(invoice);
107
103
  return;
108
104
  }
109
105
 
110
106
  logger.info('Vendor configuration found, starting fulfillment process', {
111
- paymentIntentId: paymentIntent.id,
107
+ invoiceId: invoice.id,
112
108
  });
113
109
 
114
110
  if (checkoutSession.fulfillment_status === 'completed') {
115
111
  logger.info('CheckoutSession already completed, directly trigger commission process', {
116
112
  checkoutSessionId: checkoutSession.id,
117
113
  });
118
- await triggerCommissionProcess(checkoutSession.id, paymentIntent.id);
114
+ await triggerCommissionProcess(checkoutSession.id, invoice.id);
119
115
  return;
120
116
  }
121
117
 
122
- await startVendorFulfillment(checkoutSession.id, paymentIntent.id);
118
+ await startVendorFulfillment(checkoutSession.id, invoice.id);
123
119
  } catch (error: any) {
124
120
  logger.error('Vendor commission decision failed, fallback to direct deposit vault', {
125
- paymentIntentId: job.paymentIntentId,
121
+ invoiceId: job.invoiceId,
126
122
  error,
127
123
  });
128
124
 
129
125
  if (!checkoutSession) {
130
126
  logger.error('CheckoutSession not found via any method[handleVendorCommission]', {
131
- paymentIntentId: job.paymentIntentId,
127
+ invoiceId: job.invoiceId,
132
128
  });
133
129
  return;
134
130
  }
135
131
 
136
132
  try {
137
- triggerCoordinatorCheck(checkoutSession.id, job.paymentIntentId, 'vendor_commission_decision_failed');
133
+ triggerCoordinatorCheck(checkoutSession.id, job.invoiceId, 'vendor_commission_decision_failed');
138
134
  } catch (err: any) {
139
135
  logger.error('Failed to trigger coordinator check[handleVendorCommission]', { error: err });
140
136
  }
@@ -160,23 +156,17 @@ export const startVendorCommissionQueue = async () => {
160
156
  });
161
157
 
162
158
  payments.forEach(async (x) => {
163
- const exist = await vendorCommissionQueue.get(`vendor-commission-${x.id}`);
164
- if (!exist) {
165
- vendorCommissionQueue.push({ id: x.id, job: { paymentIntentId: x.id, retryOnError: true } });
159
+ const id = `vendor-commission-${x.invoice_id}`;
160
+ const exist = await vendorCommissionQueue.get(id);
161
+ if (!exist && x.invoice_id) {
162
+ vendorCommissionQueue.push({ id, job: { invoiceId: x.invoice_id, retryOnError: true } });
166
163
  }
167
164
  });
168
165
  };
169
166
 
170
167
  events.on('invoice.paid', async (invoice) => {
171
168
  try {
172
- const paymentIntent = await PaymentIntent.findOne({ where: { invoice_id: invoice.id } });
173
-
174
- if (!paymentIntent) {
175
- logger.warn('PaymentIntent not found', { id: invoice.id });
176
- return;
177
- }
178
-
179
- const id = `vendor-commission-${paymentIntent.id}`;
169
+ const id = `vendor-commission-${invoice.id}`;
180
170
  const exist = await vendorCommissionQueue.get(id);
181
171
  if (exist) {
182
172
  logger.info('Vendor commission job already exists, skipping', { id });
@@ -185,7 +175,7 @@ events.on('invoice.paid', async (invoice) => {
185
175
 
186
176
  vendorCommissionQueue.push({
187
177
  id,
188
- job: { paymentIntentId: paymentIntent.id, retryOnError: true },
178
+ job: { invoiceId: invoice.id, retryOnError: true },
189
179
  });
190
180
  } catch (error) {
191
181
  logger.error('Failed to trigger vendor commission queue', { invoiceId: invoice.id, error });