payment-kit 1.25.0 → 1.25.1
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/api/src/libs/payment.ts +2 -0
- package/api/src/queues/payment.ts +2 -2
- package/api/src/queues/refund.ts +41 -9
- package/blocklet.yml +1 -1
- package/package.json +6 -6
package/api/src/libs/payment.ts
CHANGED
|
@@ -255,10 +255,12 @@ export async function isDelegationSufficientForPayment(args: {
|
|
|
255
255
|
const address = toDelegateAddress(delegator, wallet.address);
|
|
256
256
|
const { state } = await client.getDelegateState({ address });
|
|
257
257
|
if (!state) {
|
|
258
|
+
logger.error('isDelegationSufficientForPayment: no delegation state', { address, delegator });
|
|
258
259
|
return { sufficient: false, reason: 'NO_DELEGATION' };
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
if (!state.ops || state.ops?.length === 0) {
|
|
263
|
+
logger.error('isDelegationSufficientForPayment: no delegation ops', { address, delegator });
|
|
262
264
|
return { sufficient: false, reason: 'NO_DELEGATION' };
|
|
263
265
|
}
|
|
264
266
|
|
|
@@ -1012,12 +1012,12 @@ export const handlePayment = async (job: PaymentJob) => {
|
|
|
1012
1012
|
logger.error('PaymentIntent capture aborted - insufficient balance/delegation', {
|
|
1013
1013
|
paymentIntentId: paymentIntent.id,
|
|
1014
1014
|
invoiceId: paymentIntent.invoice_id,
|
|
1015
|
-
failureReason:
|
|
1015
|
+
failureReason: result.reason,
|
|
1016
1016
|
result,
|
|
1017
1017
|
queueDelayMs,
|
|
1018
1018
|
delaySeconds: Math.round(queueDelayMs / 1000),
|
|
1019
1019
|
});
|
|
1020
|
-
throw new CustomError(
|
|
1020
|
+
throw new CustomError(result.reason, 'Payer balance or delegation not sufficient for this payment');
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
1023
1023
|
const signed = await client.signTransferV2Tx({
|
package/api/src/queues/refund.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { isRefundReasonSupportedByStripe } from '../libs/refund';
|
|
|
3
3
|
import { checkRemainingStake, getSubscriptionStakeAddress } from '../libs/subscription';
|
|
4
4
|
import { sendErc20ToUser } from '../integrations/ethereum/token';
|
|
5
5
|
import { wallet } from '../libs/auth';
|
|
6
|
-
import CustomError from '../libs/error';
|
|
6
|
+
import CustomError, { NonRetryableError } from '../libs/error';
|
|
7
7
|
import { events } from '../libs/event';
|
|
8
8
|
import logger from '../libs/logger';
|
|
9
9
|
import { getGasPayerExtra, isBalanceSufficientForRefund } from '../libs/payment';
|
|
@@ -35,6 +35,21 @@ type Updates = {
|
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
const markRefundNonRetryable = async (refund: Refund, code: string, message: string, paymentMethod?: PaymentMethod) => {
|
|
39
|
+
await refund.update({
|
|
40
|
+
status: 'requires_action',
|
|
41
|
+
last_attempt_error: {
|
|
42
|
+
type: 'invalid_request_error',
|
|
43
|
+
code,
|
|
44
|
+
message,
|
|
45
|
+
payment_method_id: paymentMethod?.id,
|
|
46
|
+
payment_method_type: paymentMethod?.type,
|
|
47
|
+
},
|
|
48
|
+
attempt_count: refund.attempt_count + 1,
|
|
49
|
+
attempted: true,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
38
53
|
export const handleRefundFailed = (refund: Refund, error: PaymentError) => {
|
|
39
54
|
const attemptCount = refund.attempt_count + 1;
|
|
40
55
|
const updates: Updates = {
|
|
@@ -94,24 +109,27 @@ export const handleRefund = async (job: RefundJob) => {
|
|
|
94
109
|
const paymentCurrency = await PaymentCurrency.findByPk(refund.currency_id);
|
|
95
110
|
if (!paymentCurrency) {
|
|
96
111
|
logger.warn(`PaymentCurrency not found: ${refund.currency_id}`);
|
|
112
|
+
await markRefundNonRetryable(refund, 'CURRENCY_NOT_FOUND', 'Payment currency not found');
|
|
97
113
|
return;
|
|
98
114
|
}
|
|
99
115
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
100
116
|
if (!paymentMethod) {
|
|
101
117
|
logger.warn(`PaymentMethod not found: ${paymentCurrency.payment_method_id}`);
|
|
118
|
+
await markRefundNonRetryable(refund, 'PAYMENT_METHOD_NOT_FOUND', 'Payment method not found');
|
|
102
119
|
return;
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
const customer = await Customer.findByPk(refund.customer_id);
|
|
106
123
|
if (!customer) {
|
|
107
124
|
logger.warn(`Customer not found: ${refund.customer_id}`);
|
|
125
|
+
await markRefundNonRetryable(refund, 'CUSTOMER_NOT_FOUND', 'Customer not found', paymentMethod);
|
|
108
126
|
return;
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
if (refund?.type === 'stake_return') {
|
|
112
|
-
handleStakeReturnJob(job, refund, paymentCurrency, paymentMethod, customer);
|
|
130
|
+
await handleStakeReturnJob(job, refund, paymentCurrency, paymentMethod, customer);
|
|
113
131
|
} else if (paymentMethod) {
|
|
114
|
-
handleRefundJob(job, refund, paymentCurrency, paymentMethod, customer);
|
|
132
|
+
await handleRefundJob(job, refund, paymentCurrency, paymentMethod, customer);
|
|
115
133
|
}
|
|
116
134
|
};
|
|
117
135
|
|
|
@@ -164,18 +182,26 @@ const handleRefundJob = async (
|
|
|
164
182
|
const refundSupport = ['arcblock', 'ethereum', 'stripe', 'base'].includes(paymentMethod.type);
|
|
165
183
|
if (refundSupport === false) {
|
|
166
184
|
logger.warn(`PaymentMethod does not support auto charge: ${paymentCurrency.payment_method_id}`);
|
|
185
|
+
await markRefundNonRetryable(
|
|
186
|
+
refund,
|
|
187
|
+
'PAYMENT_METHOD_NOT_SUPPORTED',
|
|
188
|
+
'Payment method does not support refund',
|
|
189
|
+
paymentMethod
|
|
190
|
+
);
|
|
167
191
|
return;
|
|
168
192
|
}
|
|
169
193
|
|
|
170
194
|
// try refund transfer and reschedule on error
|
|
171
195
|
logger.info('Refund transfer attempt', { id: refund.id, attempt: refund.attempt_count });
|
|
172
|
-
let result;
|
|
173
|
-
const paymentIntent = await PaymentIntent.findByPk(refund.payment_intent_id);
|
|
174
|
-
if (!paymentIntent) {
|
|
175
|
-
throw new Error('PaymentIntent not found');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
196
|
try {
|
|
197
|
+
if (!refund.payment_intent_id) {
|
|
198
|
+
throw new NonRetryableError('PAYMENT_INTENT_NOT_FOUND', 'payment_intent_id is missing for refund');
|
|
199
|
+
}
|
|
200
|
+
const paymentIntent = await PaymentIntent.findByPk(refund.payment_intent_id);
|
|
201
|
+
if (!paymentIntent) {
|
|
202
|
+
throw new NonRetryableError('PAYMENT_INTENT_NOT_FOUND', 'PaymentIntent not found');
|
|
203
|
+
}
|
|
204
|
+
let result;
|
|
179
205
|
if (paymentMethod.type === 'arcblock') {
|
|
180
206
|
const client = paymentMethod.getOcapClient();
|
|
181
207
|
// check balance before transfer with transaction
|
|
@@ -288,6 +314,12 @@ const handleRefundJob = async (
|
|
|
288
314
|
} catch (err) {
|
|
289
315
|
logger.error('refund transfer failed', { error: err, id: refund.id });
|
|
290
316
|
|
|
317
|
+
if (err instanceof NonRetryableError || err?.nonRetryable === true) {
|
|
318
|
+
await markRefundNonRetryable(refund, err.code, err.message, paymentMethod);
|
|
319
|
+
logger.error('refund transfer aborted: non-retryable error', { id: refund.id, code: err.code });
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
291
323
|
const error: PaymentError = {
|
|
292
324
|
type: 'card_error',
|
|
293
325
|
code: err.code,
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.1",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"prelint": "npm run types",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"@blocklet/error": "^0.3.5",
|
|
60
60
|
"@blocklet/js-sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
61
61
|
"@blocklet/logger": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
62
|
-
"@blocklet/payment-broker-client": "1.25.
|
|
63
|
-
"@blocklet/payment-react": "1.25.
|
|
64
|
-
"@blocklet/payment-vendor": "1.25.
|
|
62
|
+
"@blocklet/payment-broker-client": "1.25.1",
|
|
63
|
+
"@blocklet/payment-react": "1.25.1",
|
|
64
|
+
"@blocklet/payment-vendor": "1.25.1",
|
|
65
65
|
"@blocklet/sdk": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
66
66
|
"@blocklet/ui-react": "^3.4.7",
|
|
67
67
|
"@blocklet/uploader": "^0.3.19",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"devDependencies": {
|
|
133
133
|
"@abtnode/types": "^1.17.8-beta-20260104-120132-cb5b1914",
|
|
134
134
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
135
|
-
"@blocklet/payment-types": "1.25.
|
|
135
|
+
"@blocklet/payment-types": "1.25.1",
|
|
136
136
|
"@types/cookie-parser": "^1.4.9",
|
|
137
137
|
"@types/cors": "^2.8.19",
|
|
138
138
|
"@types/debug": "^4.1.12",
|
|
@@ -179,5 +179,5 @@
|
|
|
179
179
|
"parser": "typescript"
|
|
180
180
|
}
|
|
181
181
|
},
|
|
182
|
-
"gitHead": "
|
|
182
|
+
"gitHead": "87555132c62024e5c677fd0a39df0d90be06a543"
|
|
183
183
|
}
|