Skip to content

✨ Support per-currency book price overrides (HKD/TWD)#1265

Merged
williamchong merged 1 commit into
likecoin:masterfrom
williamchong:feature/custom_pricing
May 20, 2026
Merged

✨ Support per-currency book price overrides (HKD/TWD)#1265
williamchong merged 1 commit into
likecoin:masterfrom
williamchong:feature/custom_pricing

Conversation

@williamchong
Copy link
Copy Markdown
Member

Let operations offer a bespoke off-ladder HKD/TWD price for a specific book tier without inserting a rung into the shared price ladder (which would retroactively reprice every existing book, since the ladder index is the price identity).

The override lives on the price tier doc, so it stays invisible to the publisher dropdown and is set manually via PUT .../price/:priceIndex. USD remains the stored priceInDecimal and commission base; only the Stripe charge amount per currency changes. Centralised the override resolution and the Stripe currency_options block in one pricing helper so product creation, price updates and inline checkout stay consistent; clearing an override now also reverts the synced Stripe price.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for per-currency (HKD/TWD) price overrides on NFT book price tiers, so operations can charge bespoke local-currency amounts without modifying the shared USD-indexed price ladder. The changes centralize override resolution and Stripe currency_options generation to keep Stripe product creation, Stripe price updates, and inline checkout line items consistent.

Changes:

  • Introduced priceInDecimalByCurrency on book price tiers and propagated it through types and cart/checkout data flows.
  • Added pricing helpers to resolve per-currency minor-unit amounts and to build Stripe currency_options consistently.
  • Updated Stripe product/price sync logic to recreate Stripe prices when overrides change and to allow “clearing” overrides.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/util/ValidationHelper.ts Includes priceInDecimalByCurrency in filtered price payloads.
src/util/pricing.ts Adds override-aware currency amount resolution + Stripe currency_options builder.
src/util/api/likernft/book/type.ts Extends cart item info type to carry per-currency override data.
src/util/api/likernft/book/purchase.ts Uses the new helper to compute Stripe unit_amount (incl. overrides).
src/util/api/likernft/book/index.ts Formats/validates priceInDecimalByCurrency and centralizes Stripe currency options usage.
src/util/api/likernft/book/cart.ts Carries priceInDecimalByCurrency from price tier into cart item info.
src/types/book.d.ts Defines BookPriceInDecimalByCurrency and wires it into book price types.
src/routes/likernft/book/store.ts Updates PUT price flow to sync Stripe when overrides change and attempts override clearing logic.

Comment thread src/routes/likernft/book/store.ts Outdated
Comment on lines +318 to +324
// formatPriceInfo only sets priceInDecimalByCurrency when present, so the
// spread above would otherwise retain a stale override when the caller
// clears it. Reconcile against the validated value (undefined = cleared).
if (price.priceInDecimalByCurrency) {
newPriceInfo.priceInDecimalByCurrency = price.priceInDecimalByCurrency;
} else {
delete newPriceInfo.priceInDecimalByCurrency;
Comment thread src/util/api/likernft/book/index.ts Outdated
Comment on lines +583 to +596
if (priceInDecimalByCurrencyInput !== undefined) {
if (typeof priceInDecimalByCurrencyInput !== 'object' || priceInDecimalByCurrencyInput === null) {
throw new ValidationError('INVALID_PRICE_CURRENCY_OVERRIDE');
}
const override: BookPriceInDecimalByCurrency = {};
BOOK_PRICE_OVERRIDE_CURRENCIES.forEach((currency) => {
const value = priceInDecimalByCurrencyInput[currency];
if (value === undefined) return;
if (!(typeof value === 'number' && Number.isInteger(value) && value >= 0)) {
throw new ValidationError('INVALID_PRICE_CURRENCY_OVERRIDE');
}
override[currency] = value;
});
if (Object.keys(override).length) priceInDecimalByCurrency = override;
Comment thread src/util/pricing.ts
Comment on lines +49 to +55
priceInDecimalByCurrency?: BookPriceInDecimalByCurrency,
): number {
if (currency === 'usd') return usdPriceInDecimal;
const override = priceInDecimalByCurrency?.[currency];
if (typeof override === 'number' && override > 0) return override;
return convertUSDPriceToCurrency(usdPriceInDecimal / 100, currency) * 100;
}
Comment on lines 563 to 568
isAllowCustomPrice,
isTippingEnabled: !priceInDecimal || isTippingEnabled,
order: order ?? index,
};
if (priceInDecimalByCurrency) payload.priceInDecimalByCurrency = priceInDecimalByCurrency;
if (isOwner) {
@williamchong williamchong force-pushed the feature/custom_pricing branch 2 times, most recently from d2b8f90 to 1f970f8 Compare May 20, 2026 08:53
@williamchong williamchong marked this pull request as ready for review May 20, 2026 09:32
@nwingt nwingt requested a review from Copilot May 20, 2026 15:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Comment on lines +318 to +319
if (!price.priceInDecimalByCurrency) {
delete newPriceInfo.priceInDecimalByCurrency;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is not crucial

Comment thread src/routes/likernft/book/store.ts Outdated
Comment on lines +336 to +337
if (oldPriceInfo.priceInDecimal !== newPriceInfo.priceInDecimal
|| isCurrencyOverrideChanged) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (oldPriceInfo.priceInDecimal !== newPriceInfo.priceInDecimal
|| isCurrencyOverrideChanged) {
if (
oldPriceInfo.priceInDecimal !== newPriceInfo.priceInDecimal
|| isCurrencyOverrideChanged
) {

Let operations offer a bespoke off-ladder HKD/TWD price for a specific
book tier without inserting a rung into the shared price ladder (which
would retroactively reprice every existing book, since the ladder index
is the price identity).

The override lives on the price tier doc, so it stays invisible to the
publisher dropdown and is set manually via PUT .../price/:priceIndex.
USD remains the stored priceInDecimal and commission base; only the
Stripe charge amount per currency changes. Centralised the override
resolution and the Stripe currency_options block in one pricing helper
so product creation, price updates and inline checkout stay consistent;
clearing an override now also reverts the synced Stripe price.
@williamchong williamchong force-pushed the feature/custom_pricing branch from 1f970f8 to 02cda1c Compare May 20, 2026 15:48
@williamchong williamchong merged commit 7da74fe into likecoin:master May 20, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants