payment-kit 1.13.60 → 1.13.62

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.
@@ -33,7 +33,7 @@ export async function ensureStakedForGas() {
33
33
 
34
34
  const result = await client.getForgeState({});
35
35
  const { token, txConfig } = result.state;
36
- const holding = account.tokens.find((x: any) => x.address === token.address);
36
+ const holding = (account?.tokens || []).find((x: any) => x.address === token.address);
37
37
  if (toBN(holding?.value || '0').lte(toBN(txConfig.txGas.minStake))) {
38
38
  logger.info(`not enough balance to stake for gas on chain ${host}`);
39
39
  continue;
@@ -53,3 +53,24 @@ export async function ensureStakedForGas() {
53
53
  }
54
54
  }
55
55
  }
56
+
57
+ export async function hasStakedForGas(method: PaymentMethod, sender = wallet.address) {
58
+ if (method.type === 'arcblock') {
59
+ const client = method.getOcapClient();
60
+ const address = toStakeAddress(sender, sender);
61
+ const { state } = await client.getStakeState({ address });
62
+ return !!state;
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ export async function estimateMaxGasForTx(method: PaymentMethod, typeUrl = 'fg:t:delegate') {
69
+ if (method.type === 'arcblock') {
70
+ const client = method.getOcapClient();
71
+ const { estimate } = await client.estimateGas({ typeUrl });
72
+ return estimate.max;
73
+ }
74
+
75
+ return '0';
76
+ }
@@ -20,7 +20,7 @@ const init = (label: string): Logger => {
20
20
  return instance;
21
21
  };
22
22
 
23
- const logger = process.env.NODE_ENV === 'production' ? consoleLogger : init('app');
23
+ const logger = process.env.BLOCKLET_ENV === 'production' ? consoleLogger : init('app');
24
24
 
25
25
  export default logger;
26
26
 
@@ -7,10 +7,8 @@ import { subscriptionQueue } from '../../jobs/subscription';
7
7
  import type { CallbackArgs } from '../../libs/auth';
8
8
  import { wallet } from '../../libs/auth';
9
9
  import { getGasPayerExtra } from '../../libs/payment';
10
- import { getFastCheckoutAmount } from '../../libs/session';
11
10
  import { getTxMetadata } from '../../libs/util';
12
- import type { TLineItemExpanded } from '../../store/models';
13
- import { ensureSetupIntent, getAuthPrincipalClaim } from './shared';
11
+ import { ensureSetupIntent, getAuthPrincipalClaim, getTokenRequirements } from './shared';
14
12
 
15
13
  export default {
16
14
  action: 'setup',
@@ -32,12 +30,7 @@ export default {
32
30
  }
33
31
 
34
32
  if (paymentMethod.type === 'arcblock') {
35
- const amount = getFastCheckoutAmount(
36
- checkoutSession.line_items as TLineItemExpanded[],
37
- checkoutSession.mode,
38
- paymentCurrency,
39
- !!checkoutSession.subscription_data?.trial_period_days
40
- );
33
+ const tokenRequirements = await getTokenRequirements(checkoutSession, paymentMethod, paymentCurrency);
41
34
 
42
35
  return {
43
36
  signature: {
@@ -57,7 +50,7 @@ export default {
57
50
  },
58
51
  nonce: checkoutSessionId,
59
52
  requirement: {
60
- tokens: amount === '0' ? [] : [{ address: paymentCurrency.contract as string, value: amount }],
53
+ tokens: tokenRequirements,
61
54
  },
62
55
  chainInfo: {
63
56
  host: paymentMethod.settings?.arcblock?.api_host as string,
@@ -1,9 +1,10 @@
1
1
  import { BN } from '@ocap/util';
2
2
 
3
+ import { estimateMaxGasForTx, hasStakedForGas } from '../../integrations/blockchain/stake';
3
4
  import { blocklet } from '../../libs/auth';
4
5
  import dayjs from '../../libs/dayjs';
5
6
  import logger from '../../libs/logger';
6
- import { getStatementDescriptor } from '../../libs/session';
7
+ import { getFastCheckoutAmount, getStatementDescriptor } from '../../libs/session';
7
8
  import type { TLineItemExpanded } from '../../store/models';
8
9
  import { CheckoutSession } from '../../store/models/checkout-session';
9
10
  import { Customer } from '../../store/models/customer';
@@ -450,3 +451,41 @@ export function getAuthPrincipalClaim(method: PaymentMethod, action: string) {
450
451
  chainInfo,
451
452
  };
452
453
  }
454
+
455
+ export async function getTokenRequirements(
456
+ checkoutSession: CheckoutSession,
457
+ paymentMethod: PaymentMethod,
458
+ paymentCurrency: PaymentCurrency
459
+ ) {
460
+ const tokenRequirements = [];
461
+ let amount = getFastCheckoutAmount(
462
+ checkoutSession.line_items as TLineItemExpanded[],
463
+ checkoutSession.mode,
464
+ paymentCurrency,
465
+ !!checkoutSession.subscription_data?.trial_period_days
466
+ );
467
+
468
+ // If the app has not staked, we need to add the gas fee to the amount
469
+ if ((await hasStakedForGas(paymentMethod)) === false) {
470
+ const maxGas = await estimateMaxGasForTx(paymentMethod);
471
+ // pay with the base currency? just add gas to it
472
+ if (paymentCurrency.is_base_currency) {
473
+ amount = new BN(amount).add(new BN(maxGas)).toString();
474
+ tokenRequirements.push({ address: paymentCurrency.contract as string, value: amount });
475
+ } else {
476
+ tokenRequirements.push({ address: paymentCurrency.contract as string, value: amount });
477
+ const baseCurrency = await PaymentCurrency.findOne({
478
+ where: { active: true, is_base_currency: true, payment_method_id: paymentMethod.id },
479
+ });
480
+ if (baseCurrency) {
481
+ tokenRequirements.push({ address: baseCurrency.contract as string, value: maxGas });
482
+ } else {
483
+ throw new Error('Base currency not found since app has not staked for gas');
484
+ }
485
+ }
486
+ } else {
487
+ tokenRequirements.push({ address: paymentCurrency.contract as string, value: amount });
488
+ }
489
+
490
+ return tokenRequirements;
491
+ }
@@ -9,10 +9,8 @@ import { subscriptionQueue } from '../../jobs/subscription';
9
9
  import type { CallbackArgs } from '../../libs/auth';
10
10
  import { wallet } from '../../libs/auth';
11
11
  import { getGasPayerExtra } from '../../libs/payment';
12
- import { getFastCheckoutAmount } from '../../libs/session';
13
12
  import { getTxMetadata } from '../../libs/util';
14
- import type { TLineItemExpanded } from '../../store/models';
15
- import { ensureInvoiceForCheckout, ensurePaymentIntent, getAuthPrincipalClaim } from './shared';
13
+ import { ensureInvoiceForCheckout, ensurePaymentIntent, getAuthPrincipalClaim, getTokenRequirements } from './shared';
16
14
 
17
15
  export default {
18
16
  action: 'subscription',
@@ -34,12 +32,7 @@ export default {
34
32
  }
35
33
 
36
34
  if (paymentMethod.type === 'arcblock') {
37
- const amount = getFastCheckoutAmount(
38
- checkoutSession.line_items as TLineItemExpanded[],
39
- checkoutSession.mode,
40
- paymentCurrency,
41
- !!checkoutSession.subscription_data?.trial_period_days
42
- );
35
+ const tokenRequirements = await getTokenRequirements(checkoutSession, paymentMethod, paymentCurrency);
43
36
 
44
37
  return {
45
38
  signature: {
@@ -59,7 +52,7 @@ export default {
59
52
  },
60
53
  nonce: checkoutSessionId,
61
54
  requirement: {
62
- tokens: amount === '0' ? [] : [{ address: paymentCurrency.contract as string, value: amount }],
55
+ tokens: tokenRequirements,
63
56
  },
64
57
  chainInfo: {
65
58
  host: paymentMethod.settings?.arcblock?.api_host as string,
@@ -1,14 +1,16 @@
1
+ /* eslint-disable no-console */
1
2
  /* eslint-disable no-await-in-loop */
2
3
  import type { QueryInterface } from 'sequelize';
3
4
  import { SequelizeStorage, Umzug } from 'umzug';
4
5
 
6
+ import logger from '../libs/logger';
5
7
  import { sequelize } from './sequelize';
6
8
 
7
9
  const umzug = new Umzug({
8
10
  migrations: { glob: ['migrations/*.{ts,js}', { cwd: __dirname }] },
9
11
  context: sequelize.getQueryInterface(),
10
12
  storage: new SequelizeStorage({ sequelize }),
11
- logger: console,
13
+ logger,
12
14
  });
13
15
 
14
16
  export default function migrate() {
@@ -17,14 +19,18 @@ export default function migrate() {
17
19
 
18
20
  type ColumnChanges = Record<string, { name: string; field: any }[]>;
19
21
  export async function safeApplyColumnChanges(context: QueryInterface, changes: ColumnChanges) {
22
+ console.info('safeApplyColumnChanges', changes);
20
23
  for (const [table, columns] of Object.entries(changes)) {
21
24
  const schema = await context.describeTable(table);
22
25
  for (const { name, field } of columns) {
23
26
  if (!schema[name]) {
24
27
  await context.addColumn(table, name, field);
28
+ console.info('safeApplyColumnChanges.addColumn', { table, name, field });
25
29
  if (field.defaultValue) {
26
30
  await context.bulkUpdate(table, { [name]: field.defaultValue }, {});
27
31
  }
32
+ } else {
33
+ console.info('safeApplyColumnChanges.skip', { table, name, field });
28
34
  }
29
35
  }
30
36
  }
@@ -1,52 +1,52 @@
1
1
  import type { Migration } from '../migrate';
2
2
  import models from '../models';
3
3
 
4
- export const up: Migration = async ({ context: queryInterface }) => {
5
- await queryInterface.createTable('checkout_sessions', models.CheckoutSession.GENESIS_ATTRIBUTES);
6
- await queryInterface.createTable('coupons', models.Coupon.GENESIS_ATTRIBUTES);
7
- await queryInterface.createTable('customers', models.Customer.GENESIS_ATTRIBUTES);
8
- await queryInterface.createTable('discounts', models.Discount.GENESIS_ATTRIBUTES);
9
- await queryInterface.createTable('events', models.Event.GENESIS_ATTRIBUTES);
10
- await queryInterface.createTable('invoices', models.Invoice.GENESIS_ATTRIBUTES);
11
- await queryInterface.createTable('invoice_items', models.InvoiceItem.GENESIS_ATTRIBUTES);
12
- await queryInterface.createTable('jobs', models.Job.GENESIS_ATTRIBUTES);
13
- await queryInterface.createTable('payment_currencies', models.PaymentCurrency.GENESIS_ATTRIBUTES);
14
- await queryInterface.createTable('payment_intents', models.PaymentIntent.GENESIS_ATTRIBUTES);
15
- await queryInterface.createTable('payment_links', models.PaymentLink.GENESIS_ATTRIBUTES);
16
- await queryInterface.createTable('payment_methods', models.PaymentMethod.GENESIS_ATTRIBUTES);
17
- await queryInterface.createTable('prices', models.Price.GENESIS_ATTRIBUTES);
18
- await queryInterface.createTable('products', models.Product.GENESIS_ATTRIBUTES);
19
- await queryInterface.createTable('promotion_codes', models.PromotionCode.GENESIS_ATTRIBUTES);
20
- await queryInterface.createTable('setup_intents', models.SetupIntent.GENESIS_ATTRIBUTES);
21
- await queryInterface.createTable('subscription_items', models.SubscriptionItem.GENESIS_ATTRIBUTES);
22
- await queryInterface.createTable('subscription_schedules', models.SubscriptionSchedule.GENESIS_ATTRIBUTES);
23
- await queryInterface.createTable('subscriptions', models.Subscription.GENESIS_ATTRIBUTES);
24
- await queryInterface.createTable('usage_records', models.UsageRecord.GENESIS_ATTRIBUTES);
25
- await queryInterface.createTable('webhook_attempts', models.WebhookAttempt.GENESIS_ATTRIBUTES);
26
- await queryInterface.createTable('webhook_endpoints', models.WebhookEndpoint.GENESIS_ATTRIBUTES);
4
+ export const up: Migration = async ({ context }) => {
5
+ await context.createTable('checkout_sessions', models.CheckoutSession.GENESIS_ATTRIBUTES);
6
+ await context.createTable('coupons', models.Coupon.GENESIS_ATTRIBUTES);
7
+ await context.createTable('customers', models.Customer.GENESIS_ATTRIBUTES);
8
+ await context.createTable('discounts', models.Discount.GENESIS_ATTRIBUTES);
9
+ await context.createTable('events', models.Event.GENESIS_ATTRIBUTES);
10
+ await context.createTable('invoices', models.Invoice.GENESIS_ATTRIBUTES);
11
+ await context.createTable('invoice_items', models.InvoiceItem.GENESIS_ATTRIBUTES);
12
+ await context.createTable('jobs', models.Job.GENESIS_ATTRIBUTES);
13
+ await context.createTable('payment_currencies', models.PaymentCurrency.GENESIS_ATTRIBUTES);
14
+ await context.createTable('payment_intents', models.PaymentIntent.GENESIS_ATTRIBUTES);
15
+ await context.createTable('payment_links', models.PaymentLink.GENESIS_ATTRIBUTES);
16
+ await context.createTable('payment_methods', models.PaymentMethod.GENESIS_ATTRIBUTES);
17
+ await context.createTable('prices', models.Price.GENESIS_ATTRIBUTES);
18
+ await context.createTable('products', models.Product.GENESIS_ATTRIBUTES);
19
+ await context.createTable('promotion_codes', models.PromotionCode.GENESIS_ATTRIBUTES);
20
+ await context.createTable('setup_intents', models.SetupIntent.GENESIS_ATTRIBUTES);
21
+ await context.createTable('subscription_items', models.SubscriptionItem.GENESIS_ATTRIBUTES);
22
+ await context.createTable('subscription_schedules', models.SubscriptionSchedule.GENESIS_ATTRIBUTES);
23
+ await context.createTable('subscriptions', models.Subscription.GENESIS_ATTRIBUTES);
24
+ await context.createTable('usage_records', models.UsageRecord.GENESIS_ATTRIBUTES);
25
+ await context.createTable('webhook_attempts', models.WebhookAttempt.GENESIS_ATTRIBUTES);
26
+ await context.createTable('webhook_endpoints', models.WebhookEndpoint.GENESIS_ATTRIBUTES);
27
27
  };
28
28
 
29
- export const down: Migration = async ({ context: queryInterface }) => {
30
- await queryInterface.dropTable('checkout_sessions');
31
- await queryInterface.dropTable('coupons');
32
- await queryInterface.dropTable('customers');
33
- await queryInterface.dropTable('discounts');
34
- await queryInterface.dropTable('events');
35
- await queryInterface.dropTable('invoices');
36
- await queryInterface.dropTable('invoice_items');
37
- await queryInterface.dropTable('jobs');
38
- await queryInterface.dropTable('payment_currencies');
39
- await queryInterface.dropTable('payment_intents');
40
- await queryInterface.dropTable('payment_links');
41
- await queryInterface.dropTable('payment_methods');
42
- await queryInterface.dropTable('prices');
43
- await queryInterface.dropTable('products');
44
- await queryInterface.dropTable('promotion_codes');
45
- await queryInterface.dropTable('setup_intents');
46
- await queryInterface.dropTable('subscription_items');
47
- await queryInterface.dropTable('subscription_schedules');
48
- await queryInterface.dropTable('subscriptions');
49
- await queryInterface.dropTable('usage_records');
50
- await queryInterface.dropTable('webhook_endpoints');
51
- await queryInterface.dropTable('webhook_attempts');
29
+ export const down: Migration = async ({ context }) => {
30
+ await context.dropTable('checkout_sessions');
31
+ await context.dropTable('coupons');
32
+ await context.dropTable('customers');
33
+ await context.dropTable('discounts');
34
+ await context.dropTable('events');
35
+ await context.dropTable('invoices');
36
+ await context.dropTable('invoice_items');
37
+ await context.dropTable('jobs');
38
+ await context.dropTable('payment_currencies');
39
+ await context.dropTable('payment_intents');
40
+ await context.dropTable('payment_links');
41
+ await context.dropTable('payment_methods');
42
+ await context.dropTable('prices');
43
+ await context.dropTable('products');
44
+ await context.dropTable('promotion_codes');
45
+ await context.dropTable('setup_intents');
46
+ await context.dropTable('subscription_items');
47
+ await context.dropTable('subscription_schedules');
48
+ await context.dropTable('subscriptions');
49
+ await context.dropTable('usage_records');
50
+ await context.dropTable('webhook_endpoints');
51
+ await context.dropTable('webhook_attempts');
52
52
  };
@@ -158,8 +158,8 @@ const paymentMethods = [
158
158
  },
159
159
  ];
160
160
 
161
- export const up: Migration = async ({ context: queryInterface }) => {
162
- await queryInterface.bulkInsert(
161
+ export const up: Migration = async ({ context }) => {
162
+ await context.bulkInsert(
163
163
  'payment_methods',
164
164
  paymentMethods,
165
165
  {},
@@ -170,7 +170,7 @@ export const up: Migration = async ({ context: queryInterface }) => {
170
170
  metadata: { type: new Sequelize.JSON() },
171
171
  }
172
172
  );
173
- await queryInterface.bulkInsert(
173
+ await context.bulkInsert(
174
174
  'payment_currencies',
175
175
  paymentCurrencies,
176
176
  {},
@@ -180,7 +180,7 @@ export const up: Migration = async ({ context: queryInterface }) => {
180
180
  );
181
181
  };
182
182
 
183
- export const down: Migration = async ({ context: queryInterface }) => {
184
- await queryInterface.bulkDelete('payment_methods', {});
185
- await queryInterface.bulkDelete('payment_currencies', {});
183
+ export const down: Migration = async ({ context }) => {
184
+ await context.bulkDelete('payment_methods', {});
185
+ await context.bulkDelete('payment_currencies', {});
186
186
  };
@@ -1,10 +1,10 @@
1
1
  import type { Migration } from '../migrate';
2
2
  import models from '../models';
3
3
 
4
- export const up: Migration = async ({ context: queryInterface }) => {
5
- await queryInterface.createTable('pricing_tables', models.PricingTable.GENESIS_ATTRIBUTES);
4
+ export const up: Migration = async ({ context }) => {
5
+ await context.createTable('pricing_tables', models.PricingTable.GENESIS_ATTRIBUTES);
6
6
  };
7
7
 
8
- export const down: Migration = async ({ context: queryInterface }) => {
9
- await queryInterface.dropTable('pricing_tables');
8
+ export const down: Migration = async ({ context }) => {
9
+ await context.dropTable('pricing_tables');
10
10
  };
@@ -9,7 +9,6 @@ export const up: Migration = async ({ context }) => {
9
9
  checkout_sessions: [
10
10
  { name: 'nft_mint_settings', field: { type: DataTypes.JSON, allowNull: true } },
11
11
  { name: 'payment_intent_data', field: { type: DataTypes.JSON, allowNull: true } },
12
- { name: 'nft_mint_settings', field: { type: DataTypes.JSON, allowNull: true } },
13
12
  { name: 'nft_mint_details', field: { type: DataTypes.JSON, allowNull: true } },
14
13
  {
15
14
  name: 'nft_mint_status',
@@ -10,6 +10,6 @@ import env from '../libs/env';
10
10
  // eslint-disable-next-line import/prefer-default-export
11
11
  export const sequelize = new Sequelize({
12
12
  dialect: 'sqlite',
13
- logging: false,
13
+ logging: process.env.SQL_LOG === '1',
14
14
  storage: join(env.dataDir, 'payment-kit.db'),
15
15
  });
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.13.60
17
+ version: 1.13.62
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -57,9 +57,7 @@ screenshots:
57
57
  - 5-admin-4.png
58
58
  components:
59
59
  - name: image-bin
60
- required: true
61
60
  source:
62
- store: https://test.store.blocklet.dev
63
61
  name: image-bin
64
62
  version: latest
65
63
  navigation:
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.60",
3
+ "version": "1.13.62",
4
4
  "scripts": {
5
- "dev": "blocklet dev",
5
+ "dev": "COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
6
6
  "eject": "vite eject",
7
7
  "lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
8
8
  "lint:fix": "npm run lint -- --fix",
@@ -104,7 +104,7 @@
104
104
  "@abtnode/types": "1.16.18",
105
105
  "@arcblock/eslint-config": "^0.2.4",
106
106
  "@arcblock/eslint-config-ts": "^0.2.4",
107
- "@did-pay/types": "1.13.60",
107
+ "@did-pay/types": "1.13.62",
108
108
  "@types/cookie-parser": "^1.4.6",
109
109
  "@types/cors": "^2.8.17",
110
110
  "@types/dotenv-flow": "^3.3.3",
@@ -141,5 +141,5 @@
141
141
  "parser": "typescript"
142
142
  }
143
143
  },
144
- "gitHead": "8e15e9d178ebb2c038066ae2b17f95415b6919cb"
144
+ "gitHead": "e5cd5ce0c4960a576e2ad6f6cf24e30b3cf552b8"
145
145
  }