payment-kit 1.13.72 → 1.13.73
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/jobs/checkout-session.ts +12 -1
- package/api/src/libs/payment.ts +22 -4
- package/api/src/routes/checkout-sessions.ts +4 -7
- package/api/src/routes/connect/setup.ts +8 -1
- package/api/src/routes/connect/shared.ts +4 -4
- package/api/src/routes/connect/subscribe.ts +7 -1
- package/api/src/store/migrations/20231220-setup-intent.ts +22 -0
- package/api/src/store/models/checkout-session.ts +4 -0
- package/blocklet.yml +1 -1
- package/package.json +3 -3
|
@@ -2,7 +2,7 @@ import { mintNftForCheckoutSession } from '../integrations/blockchain/nft';
|
|
|
2
2
|
import { ensurePassportIssued, ensurePassportRevoked } from '../integrations/blocklet/passport';
|
|
3
3
|
import { events } from '../libs/event';
|
|
4
4
|
import logger from '../libs/logger';
|
|
5
|
-
import
|
|
5
|
+
import { CheckoutSession, Price, Subscription } from '../store/models';
|
|
6
6
|
|
|
7
7
|
// eslint-disable-next-line require-await
|
|
8
8
|
export async function startCheckoutSessionQueue() {
|
|
@@ -13,6 +13,17 @@ export async function startCheckoutSessionQueue() {
|
|
|
13
13
|
mintNftForCheckoutSession(checkoutSession.id).catch((err) => {
|
|
14
14
|
logger.error('mintNftForCheckoutSession failed', { error: err, checkoutSession: checkoutSession.id });
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
// lock prices used
|
|
18
|
+
Price.update(
|
|
19
|
+
{ locked: true },
|
|
20
|
+
{ where: { id: checkoutSession.line_items.map((x) => x.upsell_price_id || x.price_id) } }
|
|
21
|
+
).catch((err) => {
|
|
22
|
+
logger.error('lock price on checkout session complete failed', {
|
|
23
|
+
error: err,
|
|
24
|
+
checkoutSession: checkoutSession.id,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
16
27
|
});
|
|
17
28
|
|
|
18
29
|
events.on('customer.subscription.deleted', (subscription: Subscription) => {
|
package/api/src/libs/payment.ts
CHANGED
|
@@ -8,7 +8,14 @@ import { BN, fromUnitToToken } from '@ocap/util';
|
|
|
8
8
|
import cloneDeep from 'lodash/cloneDeep';
|
|
9
9
|
import type { LiteralUnion } from 'type-fest';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
CheckoutSession,
|
|
13
|
+
Invoice,
|
|
14
|
+
PaymentCurrency,
|
|
15
|
+
PaymentIntent,
|
|
16
|
+
PaymentMethod,
|
|
17
|
+
TLineItemExpanded,
|
|
18
|
+
} from '../store/models';
|
|
12
19
|
import type { TPaymentCurrency } from '../store/models/payment-currency';
|
|
13
20
|
import { blocklet, wallet } from './auth';
|
|
14
21
|
import logger from './logger';
|
|
@@ -80,7 +87,7 @@ export async function isDelegationSufficientForPayment(args: {
|
|
|
80
87
|
|
|
81
88
|
const requested = new BN(amount);
|
|
82
89
|
const allowance = new BN(tokenLimit.txAllowance);
|
|
83
|
-
if (requested.gt(allowance)) {
|
|
90
|
+
if (tokenLimit.txAllowance !== '0' && requested.gt(allowance)) {
|
|
84
91
|
return { sufficient: false, reason: 'NO_ENOUGH_ALLOWANCE' };
|
|
85
92
|
}
|
|
86
93
|
}
|
|
@@ -181,6 +188,7 @@ export async function getPaymentDetail(userDid: string, invoice: Invoice): Promi
|
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
export async function getTokenLimitsForDelegation(
|
|
191
|
+
checkoutSession: CheckoutSession,
|
|
184
192
|
paymentMethod: PaymentMethod,
|
|
185
193
|
paymentCurrency: PaymentCurrency,
|
|
186
194
|
address: string,
|
|
@@ -189,11 +197,15 @@ export async function getTokenLimitsForDelegation(
|
|
|
189
197
|
const client = paymentMethod.getOcapClient();
|
|
190
198
|
const { state } = await client.getDelegateState({ address });
|
|
191
199
|
|
|
200
|
+
const items = checkoutSession.line_items as TLineItemExpanded[];
|
|
201
|
+
const hasMetered = items.some((x) => x.price.recurring?.usage_type === 'metered');
|
|
202
|
+
const allowance = hasMetered ? '0' : amount;
|
|
203
|
+
|
|
192
204
|
// @ts-ignore
|
|
193
205
|
const entry: TokenLimit = {
|
|
194
206
|
address: paymentCurrency.contract as string,
|
|
195
207
|
to: [wallet.address], // FIXME: may broken if we have vault, migrated
|
|
196
|
-
txAllowance:
|
|
208
|
+
txAllowance: allowance,
|
|
197
209
|
totalAllowance: '0',
|
|
198
210
|
txCount: 0,
|
|
199
211
|
validUntil: 0,
|
|
@@ -204,6 +216,11 @@ export async function getTokenLimitsForDelegation(
|
|
|
204
216
|
return [entry];
|
|
205
217
|
}
|
|
206
218
|
|
|
219
|
+
// If we have metered items, we should not limit tx allowance(set to 0)
|
|
220
|
+
if (hasMetered) {
|
|
221
|
+
return [entry];
|
|
222
|
+
}
|
|
223
|
+
|
|
207
224
|
const op = (state as DelegateState).ops.find((x) => x.key === OCAP_PAYMENT_TX_TYPE);
|
|
208
225
|
if (op && Array.isArray(op.value.limit?.tokens) && op.value.limit.tokens.length > 0) {
|
|
209
226
|
const tokenLimits = cloneDeep(op.value.limit.tokens);
|
|
@@ -212,7 +229,8 @@ export async function getTokenLimitsForDelegation(
|
|
|
212
229
|
if (index > -1) {
|
|
213
230
|
const limit = op.value.limit.tokens[index] as TokenLimit;
|
|
214
231
|
// If we have a previous delegation and the txAllowance is smaller than requested amount
|
|
215
|
-
|
|
232
|
+
// If txAllowance is 0 (unlimited), we should not update it
|
|
233
|
+
if (limit.txAllowance !== '0' && new BN(limit.txAllowance).lt(new BN(amount))) {
|
|
216
234
|
tokenLimits[index] = entry;
|
|
217
235
|
}
|
|
218
236
|
} else {
|
|
@@ -581,7 +581,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
581
581
|
payment_method_id: paymentMethod.id,
|
|
582
582
|
last_setup_error: null,
|
|
583
583
|
});
|
|
584
|
-
logger.info('
|
|
584
|
+
logger.info('setupIntent reset on checkout session submit', {
|
|
585
585
|
session: checkoutSession.id,
|
|
586
586
|
intent: setupIntent.id,
|
|
587
587
|
});
|
|
@@ -599,13 +599,13 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
599
599
|
usage: 'off_session',
|
|
600
600
|
metadata: checkoutSession.metadata,
|
|
601
601
|
});
|
|
602
|
+
|
|
603
|
+
// persist setup intent id
|
|
604
|
+
await checkoutSession.update({ setup_intent_id: setupIntent.id });
|
|
602
605
|
logger.info('setupIntent created on checkout session submit', {
|
|
603
606
|
session: checkoutSession.id,
|
|
604
607
|
intent: setupIntent.id,
|
|
605
608
|
});
|
|
606
|
-
|
|
607
|
-
// persist setup intent id
|
|
608
|
-
await checkoutSession.update({ setup_intent_id: setupIntent.id });
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
611
|
|
|
@@ -678,9 +678,6 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
|
|
|
678
678
|
items: items.map((x) => x.id),
|
|
679
679
|
});
|
|
680
680
|
|
|
681
|
-
// lock prices used by this subscription
|
|
682
|
-
await Price.update({ locked: true }, { where: { id: lineItems.map((x) => x.upsell_price_id || x.price_id) } });
|
|
683
|
-
|
|
684
681
|
// persist subscription id
|
|
685
682
|
await checkoutSession.update({ subscription_id: subscription.id });
|
|
686
683
|
}
|
|
@@ -38,7 +38,14 @@ export default {
|
|
|
38
38
|
checkoutSession.mode,
|
|
39
39
|
paymentCurrency
|
|
40
40
|
);
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
const tokenLimits = await getTokenLimitsForDelegation(
|
|
43
|
+
checkoutSession,
|
|
44
|
+
paymentMethod,
|
|
45
|
+
paymentCurrency,
|
|
46
|
+
address,
|
|
47
|
+
amount
|
|
48
|
+
);
|
|
42
49
|
const tokenRequirements = await getTokenRequirements(checkoutSession, paymentMethod, paymentCurrency);
|
|
43
50
|
|
|
44
51
|
return {
|
|
@@ -135,16 +135,16 @@ export async function ensureSetupIntent(checkoutSessionId: string, userDid?: str
|
|
|
135
135
|
if (checkoutSession.setup_intent_id) {
|
|
136
136
|
setupIntent = await SetupIntent.findByPk(checkoutSession.setup_intent_id);
|
|
137
137
|
if (!setupIntent) {
|
|
138
|
-
throw new Error('
|
|
138
|
+
throw new Error('Setup intent not found');
|
|
139
139
|
}
|
|
140
140
|
if (setupIntent.status === 'succeeded') {
|
|
141
|
-
throw new Error('
|
|
141
|
+
throw new Error('Setup intent completed');
|
|
142
142
|
}
|
|
143
143
|
if (setupIntent.status === 'canceled') {
|
|
144
|
-
throw new Error('
|
|
144
|
+
throw new Error('Setup intent canceled');
|
|
145
145
|
}
|
|
146
146
|
if (setupIntent.status === 'processing') {
|
|
147
|
-
throw new Error('
|
|
147
|
+
throw new Error('Setup intent processing');
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
customerId = setupIntent.customer_id;
|
|
@@ -40,7 +40,13 @@ export default {
|
|
|
40
40
|
checkoutSession.mode,
|
|
41
41
|
paymentCurrency
|
|
42
42
|
);
|
|
43
|
-
const tokenLimits = await getTokenLimitsForDelegation(
|
|
43
|
+
const tokenLimits = await getTokenLimitsForDelegation(
|
|
44
|
+
checkoutSession,
|
|
45
|
+
paymentMethod,
|
|
46
|
+
paymentCurrency,
|
|
47
|
+
address,
|
|
48
|
+
amount
|
|
49
|
+
);
|
|
44
50
|
const tokenRequirements = await getTokenRequirements(checkoutSession, paymentMethod, paymentCurrency);
|
|
45
51
|
|
|
46
52
|
return {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
import { DataTypes } from 'sequelize';
|
|
3
|
+
|
|
4
|
+
import { Migration, safeApplyColumnChanges } from '../migrate';
|
|
5
|
+
|
|
6
|
+
export const up: Migration = async ({ context }) => {
|
|
7
|
+
await safeApplyColumnChanges(context, {
|
|
8
|
+
checkout_sessions: [
|
|
9
|
+
{
|
|
10
|
+
name: 'setup_intent_id',
|
|
11
|
+
field: {
|
|
12
|
+
type: DataTypes.STRING(30),
|
|
13
|
+
allowNull: true,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const down: Migration = async ({ context }) => {
|
|
21
|
+
await context.removeColumn('checkout_sessions', 'setup_intent_id');
|
|
22
|
+
};
|
|
@@ -413,6 +413,10 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
|
|
|
413
413
|
type: DataTypes.ENUM('auto', 'required'),
|
|
414
414
|
defaultValue: 'auto',
|
|
415
415
|
},
|
|
416
|
+
setup_intent_id: {
|
|
417
|
+
type: DataTypes.STRING(30),
|
|
418
|
+
allowNull: true,
|
|
419
|
+
},
|
|
416
420
|
},
|
|
417
421
|
{
|
|
418
422
|
sequelize,
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.73",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"@abtnode/types": "^1.16.19",
|
|
107
107
|
"@arcblock/eslint-config": "^0.2.4",
|
|
108
108
|
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
109
|
-
"@did-pay/types": "1.13.
|
|
109
|
+
"@did-pay/types": "1.13.73",
|
|
110
110
|
"@types/cookie-parser": "^1.4.6",
|
|
111
111
|
"@types/cors": "^2.8.17",
|
|
112
112
|
"@types/dotenv-flow": "^3.3.3",
|
|
@@ -143,5 +143,5 @@
|
|
|
143
143
|
"parser": "typescript"
|
|
144
144
|
}
|
|
145
145
|
},
|
|
146
|
-
"gitHead": "
|
|
146
|
+
"gitHead": "544382d01feaed289ddaf2066b701a6ecf253370"
|
|
147
147
|
}
|