payment-kit 1.15.7 → 1.15.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.
- package/api/src/crons/currency.ts +36 -0
- package/api/src/hooks/pre-start.ts +2 -0
- package/api/src/index.ts +2 -0
- package/api/src/libs/notification/template/customer-reward-succeeded.ts +10 -0
- package/api/src/libs/notification/template/subscription-will-renew.ts +30 -24
- package/api/src/libs/payment.ts +1 -0
- package/api/src/routes/invoices.ts +8 -7
- package/api/src/routes/prices.ts +1 -1
- package/blocklet.yml +1 -1
- package/package.json +10 -10
- package/src/components/product/create.tsx +2 -2
- package/src/contexts/products.tsx +1 -1
- package/src/pages/customer/index.tsx +1 -1
- package/src/pages/customer/invoice/past-due.tsx +1 -1
- package/src/pages/customer/subscription/change-payment.tsx +1 -0
- package/src/pages/customer/subscription/embed.tsx +15 -10
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Op } from 'sequelize';
|
|
2
|
+
import { getUrl } from '@blocklet/sdk';
|
|
3
|
+
import { PaymentMethod, PaymentCurrency } from '../store/models';
|
|
4
|
+
import logger from '../libs/logger';
|
|
5
|
+
|
|
6
|
+
export async function syncCurrencyLogo() {
|
|
7
|
+
const where = {
|
|
8
|
+
logo: {
|
|
9
|
+
[Op.like]: '%/methods/%.png',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const [paymentMethods, paymentCurrencies] = await Promise.all([
|
|
14
|
+
PaymentMethod.findAll({ where }),
|
|
15
|
+
PaymentCurrency.findAll({ where }),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const promises: Promise<any>[] = [];
|
|
19
|
+
|
|
20
|
+
const updateLogo = (item: PaymentMethod | PaymentCurrency) => {
|
|
21
|
+
const { logo } = item;
|
|
22
|
+
if (/\/methods\/(stripe|arcblock|ethereum)\.png$/.test(logo)) {
|
|
23
|
+
const newLogo = getUrl(logo.replace(/^.*?(\/methods\/.*)$/, '$1'));
|
|
24
|
+
promises.push((item as any).update({ logo: newLogo }));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
paymentMethods.forEach(updateLogo);
|
|
29
|
+
paymentCurrencies.forEach(updateLogo);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await Promise.all(promises);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error('syncCurrency error', error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -6,6 +6,7 @@ import { ensurePaymentStats } from '../crons/payment-stat';
|
|
|
6
6
|
import { initPaywallResources } from '../libs/resource';
|
|
7
7
|
import { initialize } from '../store/models';
|
|
8
8
|
import { sequelize } from '../store/sequelize';
|
|
9
|
+
import { syncCurrencyLogo } from '../crons/currency';
|
|
9
10
|
|
|
10
11
|
dotenv.config({ silent: true });
|
|
11
12
|
|
|
@@ -14,6 +15,7 @@ dotenv.config({ silent: true });
|
|
|
14
15
|
initialize(sequelize);
|
|
15
16
|
await initPaywallResources();
|
|
16
17
|
await ensurePaymentStats();
|
|
18
|
+
await syncCurrencyLogo();
|
|
17
19
|
process.exit(0);
|
|
18
20
|
} catch (err) {
|
|
19
21
|
console.error('pre-start error', err);
|
package/api/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import morgan from 'morgan';
|
|
|
11
11
|
|
|
12
12
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
13
13
|
import { xss } from '@blocklet/xss';
|
|
14
|
+
import { csrf } from '@blocklet/sdk/lib/middlewares';
|
|
14
15
|
|
|
15
16
|
import crons from './crons/index';
|
|
16
17
|
import { ensureStakedForGas } from './integrations/arcblock/stake';
|
|
@@ -57,6 +58,7 @@ app.use((req, res, next) => {
|
|
|
57
58
|
app.use(express.urlencoded({ extended: true, limit: '1 mb' }));
|
|
58
59
|
app.use(cors());
|
|
59
60
|
app.use(xss({ allowedKeys: [] }));
|
|
61
|
+
app.use(csrf());
|
|
60
62
|
app.use(ensureI18n());
|
|
61
63
|
|
|
62
64
|
const router = express.Router();
|
|
@@ -44,6 +44,7 @@ interface CustomerRewardSucceededEmailTemplateContext {
|
|
|
44
44
|
title: string;
|
|
45
45
|
appDID: string;
|
|
46
46
|
logo?: string;
|
|
47
|
+
desc?: string;
|
|
47
48
|
}[]
|
|
48
49
|
| null;
|
|
49
50
|
donationSettings: DonationSettings;
|
|
@@ -187,6 +188,7 @@ export class CustomerRewardSucceededEmailTemplate
|
|
|
187
188
|
title: string;
|
|
188
189
|
appDID: string;
|
|
189
190
|
logo?: string;
|
|
191
|
+
desc?: string;
|
|
190
192
|
}[]
|
|
191
193
|
| null
|
|
192
194
|
> {
|
|
@@ -215,6 +217,7 @@ export class CustomerRewardSucceededEmailTemplate
|
|
|
215
217
|
? joinURL(process.env.BLOCKLET_APP_URL, users[index]?.user?.avatar as string)
|
|
216
218
|
: '',
|
|
217
219
|
appDID: x.address,
|
|
220
|
+
desc: '',
|
|
218
221
|
};
|
|
219
222
|
});
|
|
220
223
|
|
|
@@ -296,6 +299,13 @@ export class CustomerRewardSucceededEmailTemplate
|
|
|
296
299
|
text: translate('notification.common.rewardDetail', locale),
|
|
297
300
|
},
|
|
298
301
|
},
|
|
302
|
+
{
|
|
303
|
+
type: 'text',
|
|
304
|
+
data: {
|
|
305
|
+
type: 'plain',
|
|
306
|
+
text: ' ', // TODO: ios bug fix, wait for next release
|
|
307
|
+
},
|
|
308
|
+
},
|
|
299
309
|
].filter(Boolean),
|
|
300
310
|
},
|
|
301
311
|
...(rewardDetail
|
|
@@ -100,11 +100,14 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
100
100
|
// paymentDetail.price = +amount;
|
|
101
101
|
|
|
102
102
|
const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
|
|
103
|
-
const paymentDetail = { price: paymentAmount, balance: 0, symbol: paymentCurrency.symbol };
|
|
103
|
+
const paymentDetail = { price: paymentAmount, balance: 0, symbol: paymentCurrency.symbol, balanceFormatted: '0' };
|
|
104
104
|
|
|
105
105
|
const token = await getTokenSummaryByDid(userDid, customer.livemode);
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
const balance = fromUnitToToken(token?.[paymentCurrency.id] || '0', paymentCurrency.decimal);
|
|
108
|
+
paymentDetail.balanceFormatted = balance;
|
|
109
|
+
paymentDetail.balance = +balance;
|
|
110
|
+
|
|
108
111
|
const { isPrePaid, interval } = await this.getPaymentCategory({
|
|
109
112
|
subscriptionId: subscription.id,
|
|
110
113
|
});
|
|
@@ -173,9 +176,10 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
173
176
|
|
|
174
177
|
const lineItemExpanded = await Price.expand(subscriptionItems);
|
|
175
178
|
const metered = lineItemExpanded.find((lineItem) => lineItem.price.recurring?.usage_type === 'metered');
|
|
179
|
+
const recurringItems = lineItemExpanded.filter((lineItem) => lineItem.price.type === 'recurring');
|
|
176
180
|
|
|
177
181
|
const isPrePaid = !metered;
|
|
178
|
-
const interval =
|
|
182
|
+
const interval = recurringItems?.[0]?.price.recurring?.interval;
|
|
179
183
|
|
|
180
184
|
return {
|
|
181
185
|
isPrePaid,
|
|
@@ -269,7 +273,7 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
269
273
|
at,
|
|
270
274
|
productName,
|
|
271
275
|
willRenewDuration,
|
|
272
|
-
balance: `${paymentDetail.
|
|
276
|
+
balance: `${paymentDetail.balanceFormatted} ${paymentDetail.symbol}`,
|
|
273
277
|
})}`
|
|
274
278
|
: `${translate('notification.subscriptionWillRenew.unableToPayBody', locale, {
|
|
275
279
|
at,
|
|
@@ -326,25 +330,28 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
326
330
|
text: paymentInfo,
|
|
327
331
|
},
|
|
328
332
|
},
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
333
|
+
...(!isStripe
|
|
334
|
+
? [
|
|
335
|
+
{
|
|
336
|
+
type: 'text',
|
|
337
|
+
data: {
|
|
338
|
+
type: 'plain',
|
|
339
|
+
color: '#9397A1',
|
|
340
|
+
text: translate('notification.common.currentBalance', locale),
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
type: 'text',
|
|
345
|
+
data: {
|
|
346
|
+
type: 'plain',
|
|
347
|
+
...(!canPay && {
|
|
348
|
+
color: '#FF0000',
|
|
349
|
+
}),
|
|
350
|
+
text: `${paymentDetail.balanceFormatted} ${paymentDetail.symbol}`,
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
: []),
|
|
348
355
|
...(!canPay && !isStripe
|
|
349
356
|
? [
|
|
350
357
|
{
|
|
@@ -368,7 +375,6 @@ export class SubscriptionWillRenewEmailTemplate
|
|
|
368
375
|
},
|
|
369
376
|
]
|
|
370
377
|
: []),
|
|
371
|
-
|
|
372
378
|
{
|
|
373
379
|
type: 'text',
|
|
374
380
|
data: {
|
package/api/src/libs/payment.ts
CHANGED
|
@@ -134,13 +134,14 @@ router.get('/', authMine, async (req, res) => {
|
|
|
134
134
|
const client = method.getOcapClient();
|
|
135
135
|
const { address } = subscription.payment_details.arcblock.staking;
|
|
136
136
|
const { state } = await client.getStakeState({ address });
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
const firstInvoice = await Invoice.findOne({
|
|
138
|
+
where: { subscription_id: subscription.id },
|
|
139
|
+
order: [['created_at', 'ASC']],
|
|
140
|
+
});
|
|
141
|
+
const last = query.o === 'asc' ? list?.[list.length - 1] : list?.[0];
|
|
142
|
+
if (state && firstInvoice) {
|
|
142
143
|
const data = JSON.parse(state.data?.value || '{}');
|
|
143
|
-
const customer = await Customer.findByPk(
|
|
144
|
+
const customer = await Customer.findByPk(firstInvoice.customer_id);
|
|
144
145
|
const currency = await PaymentCurrency.findOne({
|
|
145
146
|
where: { payment_method_id: method.id, is_base_currency: true },
|
|
146
147
|
});
|
|
@@ -178,7 +179,7 @@ router.get('/', authMine, async (req, res) => {
|
|
|
178
179
|
amount_due: '0',
|
|
179
180
|
amount_paid: stakeAmount,
|
|
180
181
|
amount_remaining: '0',
|
|
181
|
-
...pick(
|
|
182
|
+
...pick(firstInvoice, [
|
|
182
183
|
'number',
|
|
183
184
|
'paid',
|
|
184
185
|
'auto_advance',
|
package/api/src/routes/prices.ts
CHANGED
|
@@ -305,7 +305,7 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
305
305
|
pick(
|
|
306
306
|
req.body,
|
|
307
307
|
locked
|
|
308
|
-
? ['nickname', 'description', 'metadata', 'currency_options', 'upsell', ...quantityKeys]
|
|
308
|
+
? ['nickname', 'description', 'metadata', 'currency_options', 'upsell', 'lookup_key', ...quantityKeys]
|
|
309
309
|
: ['type', 'model', 'active', 'livemode', 'nickname', 'recurring', 'description', 'tiers', 'unit_amount', 'transform_quantity', 'metadata', 'lookup_key', 'currency_options', 'upsell', ...quantityKeys] // prettier-ignore
|
|
310
310
|
)
|
|
311
311
|
);
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.9",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@abtnode/cron": "1.16.
|
|
45
|
+
"@abtnode/cron": "^1.16.31",
|
|
46
46
|
"@arcblock/did": "^1.18.135",
|
|
47
47
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
48
48
|
"@arcblock/did-connect": "^2.10.33",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@arcblock/jwt": "^1.18.135",
|
|
51
51
|
"@arcblock/ux": "^2.10.33",
|
|
52
52
|
"@arcblock/validator": "^1.18.135",
|
|
53
|
-
"@blocklet/js-sdk": "1.16.
|
|
54
|
-
"@blocklet/logger": "1.16.
|
|
55
|
-
"@blocklet/payment-react": "1.15.
|
|
56
|
-
"@blocklet/sdk": "1.16.
|
|
53
|
+
"@blocklet/js-sdk": "^1.16.31",
|
|
54
|
+
"@blocklet/logger": "^1.16.31",
|
|
55
|
+
"@blocklet/payment-react": "1.15.9",
|
|
56
|
+
"@blocklet/sdk": "^1.16.31",
|
|
57
57
|
"@blocklet/ui-react": "^2.10.33",
|
|
58
|
-
"@blocklet/uploader": "^0.1.
|
|
58
|
+
"@blocklet/uploader": "^0.1.36",
|
|
59
59
|
"@blocklet/xss": "^0.1.7",
|
|
60
60
|
"@mui/icons-material": "^5.16.6",
|
|
61
61
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
"validator": "^13.12.0"
|
|
117
117
|
},
|
|
118
118
|
"devDependencies": {
|
|
119
|
-
"@abtnode/types": "1.16.
|
|
119
|
+
"@abtnode/types": "^1.16.31",
|
|
120
120
|
"@arcblock/eslint-config-ts": "^0.3.2",
|
|
121
|
-
"@blocklet/payment-types": "1.15.
|
|
121
|
+
"@blocklet/payment-types": "1.15.9",
|
|
122
122
|
"@types/cookie-parser": "^1.4.7",
|
|
123
123
|
"@types/cors": "^2.8.17",
|
|
124
124
|
"@types/debug": "^4.1.12",
|
|
@@ -160,5 +160,5 @@
|
|
|
160
160
|
"parser": "typescript"
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
|
-
"gitHead": "
|
|
163
|
+
"gitHead": "d2919fe52ba2b7aee1c6c1275c325f91b81027da"
|
|
164
164
|
}
|
|
@@ -29,7 +29,7 @@ export default function CreateProduct({
|
|
|
29
29
|
name: '',
|
|
30
30
|
description: '',
|
|
31
31
|
images: [],
|
|
32
|
-
statement_descriptor:
|
|
32
|
+
statement_descriptor: '',
|
|
33
33
|
unit_label: '',
|
|
34
34
|
features: [],
|
|
35
35
|
prices: [{ ...DEFAULT_PRICE, currency_id: settings.baseCurrency.id }],
|
|
@@ -47,7 +47,7 @@ export default function CreateProduct({
|
|
|
47
47
|
|
|
48
48
|
const onCreate = (data: Product) => {
|
|
49
49
|
setState({ loading: true });
|
|
50
|
-
api
|
|
50
|
+
return api
|
|
51
51
|
.post('/api/products', data)
|
|
52
52
|
.then(() => {
|
|
53
53
|
Toast.success(t('admin.product.saved'));
|
|
@@ -15,7 +15,7 @@ const { Provider, Consumer } = ProductsContext;
|
|
|
15
15
|
|
|
16
16
|
const getProducts = async (): Promise<TProductExpanded[]> => {
|
|
17
17
|
// FIXME: pagination here
|
|
18
|
-
const { data } = await api.get('/api/products?active=true&page=1&pageSize=100');
|
|
18
|
+
const { data } = await api.get('/api/products?active=true&page=1&pageSize=100&donation=hide');
|
|
19
19
|
return data.list || [];
|
|
20
20
|
};
|
|
21
21
|
|
|
@@ -30,7 +30,7 @@ export default function CustomerInvoicePastDue() {
|
|
|
30
30
|
const { loading, error, data, runAsync } = useRequest(fetchData);
|
|
31
31
|
|
|
32
32
|
useEffect(() => {
|
|
33
|
-
events.
|
|
33
|
+
events.on('switch-did', () => {
|
|
34
34
|
runAsync().catch(console.error);
|
|
35
35
|
});
|
|
36
36
|
}, []);
|
|
@@ -60,16 +60,21 @@ export default function SubscriptionEmbed() {
|
|
|
60
60
|
const authToken = params.get('authToken') || '';
|
|
61
61
|
const defaultPageSize = useDefaultPageSize(20);
|
|
62
62
|
const { data: subscription, error, loading } = useRequest(() => fetchSubscriptionData(subscriptionId, authToken));
|
|
63
|
-
const { data } = useRequest(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
const { data } = useRequest(
|
|
64
|
+
() =>
|
|
65
|
+
fetchInvoiceData({
|
|
66
|
+
page: 1,
|
|
67
|
+
pageSize: defaultPageSize,
|
|
68
|
+
status: 'open,paid,uncollectible',
|
|
69
|
+
subscription_id: subscriptionId,
|
|
70
|
+
authToken,
|
|
71
|
+
ignore_zero: true,
|
|
72
|
+
include_staking: true,
|
|
73
|
+
customer_id: subscription?.customer_id,
|
|
74
|
+
}),
|
|
75
|
+
{
|
|
76
|
+
refreshDeps: [subscriptionId, authToken, subscription?.customer_id],
|
|
77
|
+
}
|
|
73
78
|
);
|
|
74
79
|
|
|
75
80
|
const subscriptionPageUrl: string = useMemo(() => {
|