payment-kit 1.14.13 → 1.14.15

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.
@@ -83,6 +83,10 @@ export async function validateInventory(line_items: LineItem[], includePendingQu
83
83
  const priceId = item.price_id;
84
84
  const quantity = Number(item.quantity || 0);
85
85
 
86
+ if (quantity < 1) {
87
+ throw new Error(`Quantity should be greater or equal to 1 for price: ${priceId}`);
88
+ }
89
+
86
90
  const price = await Price.findOne({ where: { id: priceId } });
87
91
 
88
92
  let delta = quantity;
@@ -114,6 +114,11 @@ export async function createPaymentLink(payload: any) {
114
114
  throw new Error('line_items should not be empty for payment link');
115
115
  }
116
116
 
117
+ const invalidQuantityIndex = raw.line_items.findIndex((x) => Number(x?.quantity || 0) < 1);
118
+ if (invalidQuantityIndex > -1) {
119
+ throw new Error(`line_items[${invalidQuantityIndex}].quantity should be greater or equal to 1`);
120
+ }
121
+
117
122
  const items = await Price.expand(raw.line_items);
118
123
  if (items.find((x) => x.price.custom_unit_amount) && items.length > 1) {
119
124
  throw new Error('Multiple items with custom unit amount are not supported in payment link');
@@ -172,6 +172,18 @@ const priceQuantitySchema = Joi.object({
172
172
  quantity_limit_per_checkout: Joi.number().integer().min(0).optional().default(0),
173
173
  });
174
174
 
175
+ const priceAmountSchema = Joi.object({
176
+ currency_options: Joi.array()
177
+ .items(
178
+ Joi.object({
179
+ unit_amount: Joi.number().greater(0).required(),
180
+ // 其他属性
181
+ }).unknown(true)
182
+ )
183
+ .optional(),
184
+ unit_amount: Joi.number().greater(0).required(),
185
+ });
186
+
175
187
  // FIXME: @wangshijun use schema validation
176
188
  // create price
177
189
  // eslint-disable-next-line consistent-return
@@ -183,6 +195,10 @@ router.post('/', auth, async (req, res) => {
183
195
  if (error) {
184
196
  return res.status(400).json({ error: `Price create request invalid: ${error.message}` });
185
197
  }
198
+ const { error: priceAmountError } = priceAmountSchema.validate(pick(req.body, ['currency_options', 'unit_amount']));
199
+ if (priceAmountError) {
200
+ return res.status(400).json({ error: `Price create request invalid: ${priceAmountError.message}` });
201
+ }
186
202
  const result = await createPrice({
187
203
  ...req.body,
188
204
  livemode: !!req.livemode,
@@ -248,6 +264,11 @@ router.put('/:id', auth, async (req, res) => {
248
264
  if (error) {
249
265
  return res.status(400).json({ error: `Price update request invalid: ${error.message}` });
250
266
  }
267
+
268
+ const { error: priceAmountError } = priceAmountSchema.validate(pick(req.body, ['currency_options', 'unit_amount']));
269
+ if (priceAmountError) {
270
+ return res.status(400).json({ error: `Price update request invalid: ${priceAmountError.message}` });
271
+ }
251
272
  const doc = await Price.findByPkOrLookupKey(req.params.id as string);
252
273
 
253
274
  if (!doc) {
@@ -18,6 +18,24 @@ const router = Router();
18
18
 
19
19
  const auth = authenticate<Product>({ component: true, roles: ['owner', 'admin'] });
20
20
 
21
+ const priceAmountSchema = Joi.object({
22
+ prices: Joi.array()
23
+ .items(
24
+ Joi.object({
25
+ currency_options: Joi.array()
26
+ .items(
27
+ Joi.object({
28
+ unit_amount: Joi.number().greater(0).required(),
29
+ // 其他属性
30
+ }).unknown(true)
31
+ )
32
+ .optional(),
33
+ unit_amount: Joi.number().greater(0).required(),
34
+ }).unknown(true)
35
+ )
36
+ .required(),
37
+ });
38
+
21
39
  export async function createProductAndPrices(payload: any) {
22
40
  const raw: Partial<Product> = pick(payload, [
23
41
  'name',
@@ -107,8 +125,13 @@ export async function createProductAndPrices(payload: any) {
107
125
 
108
126
  // FIXME: @wangshijun use schema validation
109
127
  // create product and price
128
+ // eslint-disable-next-line consistent-return
110
129
  router.post('/', auth, async (req, res) => {
111
130
  try {
131
+ const { error: priceAmountError } = priceAmountSchema.validate(pick(req.body, ['prices']));
132
+ if (priceAmountError) {
133
+ return res.status(400).json({ error: `Product create request invalid: ${priceAmountError.message}` });
134
+ }
112
135
  const result = await createProductAndPrices({
113
136
  ...req.body,
114
137
  active: true,
@@ -121,7 +144,7 @@ router.post('/', auth, async (req, res) => {
121
144
  res.json(result);
122
145
  } catch (err) {
123
146
  logger.error('create product error', err);
124
- res.status(400).json({ error: err.message });
147
+ return res.status(400).json({ error: err.message });
125
148
  }
126
149
  });
127
150
 
@@ -251,7 +251,7 @@ router.put('/:id/cancel', authPortal, async (req, res) => {
251
251
  if (req.user?.via === 'portal') {
252
252
  updates.cancel_at_period_end = true;
253
253
  updates.cancel_at = subscription.current_period_end;
254
- updates.cancelation_details = { reason: 'cancellation_requested', feedback, comment };
254
+ updates.cancelation_details = { reason: 'cancellation_requested', feedback, comment, return_stake: canReturnStake };
255
255
  updates.canceled_at = now;
256
256
  await addSubscriptionJob(subscription, 'cancel', true, updates.cancel_at);
257
257
  } else {
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.14.13
17
+ version: 1.14.15
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -44,6 +44,7 @@ interfaces:
44
44
  ignoreUrls:
45
45
  - /api/did/**
46
46
  - /api/integrations/stripe/webhook
47
+ - /api/products/**
47
48
  blockUnauthorized: false
48
49
  community: ''
49
50
  documentation: ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.14.13",
3
+ "version": "1.14.15",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -52,7 +52,7 @@
52
52
  "@arcblock/validator": "^1.18.127",
53
53
  "@blocklet/js-sdk": "1.16.28",
54
54
  "@blocklet/logger": "1.16.28",
55
- "@blocklet/payment-react": "1.14.13",
55
+ "@blocklet/payment-react": "1.14.15",
56
56
  "@blocklet/sdk": "1.16.28",
57
57
  "@blocklet/ui-react": "^2.10.11",
58
58
  "@blocklet/uploader": "^0.1.20",
@@ -118,7 +118,7 @@
118
118
  "devDependencies": {
119
119
  "@abtnode/types": "1.16.28",
120
120
  "@arcblock/eslint-config-ts": "^0.3.2",
121
- "@blocklet/payment-types": "1.14.13",
121
+ "@blocklet/payment-types": "1.14.15",
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": "80711d01804ae23dfa655a1673a7e182c3f040f1"
163
+ "gitHead": "a2bc07ca48172f040a2874ee1b4bb751cc41e873"
164
164
  }
@@ -80,12 +80,19 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
80
80
  render={({ field }) => (
81
81
  <Stack direction="row" alignItems="center" mt={1}>
82
82
  <TextField
83
- sx={{ width: 40, mr: 1 }}
83
+ sx={{ width: 80, mr: 1 }}
84
84
  inputProps={{ style: { padding: '4px 8px' } }}
85
85
  size="small"
86
+ type="number"
86
87
  {...field}
88
+ onChange={(e) => {
89
+ const intValue = parseInt(e.target.value || '1', 10);
90
+ if (intValue >= 1) {
91
+ field.onChange(intValue);
92
+ }
93
+ }}
87
94
  />
88
- <FormLabel style={{ marginBottom: 0 }}>Quantity</FormLabel>
95
+ <FormLabel style={{ marginBottom: 0 }}>{t('common.quantity')}</FormLabel>
89
96
  </Stack>
90
97
  )}
91
98
  />
@@ -127,7 +127,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
127
127
  const isLocked = priceLocked && window.blocklet?.PAYMENT_CHANGE_LOCKED_PRICE !== '1';
128
128
 
129
129
  const validateAmount = (v: number, currency: { maximum_precision?: number }) => {
130
- if (Number(v) < 0) {
130
+ if (Number(v) <= 0) {
131
131
  return t('admin.price.unit_amount.positive');
132
132
  }
133
133
  const validPrecision = formatAmountPrecisionLimit(v.toString(), locale, currency?.maximum_precision || 6);
@@ -201,6 +201,15 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
201
201
  </InputAdornment>
202
202
  ),
203
203
  }}
204
+ onChange={(e) => {
205
+ const { value } = e.target;
206
+ field.onChange(value);
207
+ const index = currencies.fields.findIndex((x: any) => x.currency_id === settings.baseCurrency.id);
208
+ if (index === -1) {
209
+ return;
210
+ }
211
+ setValue(getFieldName(`currency_options.${index}.unit_amount`), value, { shouldValidate: true });
212
+ }}
204
213
  />
205
214
  </Box>
206
215
  )}
@@ -132,7 +132,7 @@ export default function PriceActions({ data, onChange, variant, setAsDefault }:
132
132
  disabled: true,
133
133
  },
134
134
  { label: t('admin.paymentLink.add'), handler: onCreatePaymentLink, color: 'primary' },
135
- { label: t('admin.pricingTable.add'), handler: onCreatePricingTable, color: 'primary' },
135
+ { label: t('admin.pricingTable.add'), handler: onCreatePricingTable, color: 'primary', disabled: !data.recurring },
136
136
  ];
137
137
 
138
138
  if (setAsDefault) {