@wix/himalaya-cli 0.1367.0 → 0.1369.0
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/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/mapping-source.d.ts +3 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-contract.d.ts +44 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-wire.d.ts +14 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments.d.ts +9 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/apple-iap-contract.d.ts +18 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/apple-iap-core.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/checkout-source.d.ts +8 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-core.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/iap-order-backend.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/index.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/order-refs.d.ts +5 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +167 -41
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +75 -21
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +147 -30
- package/dist/cli.mjs +51 -15
- package/guides/himi-json-output.md +6 -4
- package/guides/iap-screen-adoption.md +3 -2
- package/guides/sdk-reference.json +57 -9
- package/package.json +2 -2
- package/templates/beauty-salon/dev/index.ts +49 -1
- package/templates/beauty-salon/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +68 -5
- package/templates/branded-lite/dev/index.ts +49 -1
- package/templates/branded-lite/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/branded-lite/tier5-src/himi-store-product/index.ts +68 -5
- package/templates/community-club/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/events-meetups/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/gym-fitness/dev/index.ts +49 -1
- package/templates/gym-fitness/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +68 -5
- package/templates/nonprofit-cause/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/podcast-creator/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/portfolio-creative/dev/index.ts +49 -1
- package/templates/portfolio-creative/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +68 -5
- package/templates/restaurant/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/retail-store/dev/index.ts +49 -1
- package/templates/retail-store/tier5-src/_shared/iap-presentation.ts +4 -2
- package/templates/retail-store/tier5-src/himi-store-product/index.ts +68 -5
- package/templates/yoga-studio/tier5-src/_shared/iap-presentation.ts +4 -2
|
@@ -1958,11 +1958,23 @@
|
|
|
1958
1958
|
"jsdoc": "/** How the App Store sells one vertical's Wix item: the purchases catalog's `appProductId` for it, and\n * the only product type the purchase flow sells it as. */",
|
|
1959
1959
|
"decl": "export interface AppleSaleRule {\n appProductId(entityId: string): string;\n productType: \"NON_CONSUMABLE\";\n}"
|
|
1960
1960
|
},
|
|
1961
|
+
{
|
|
1962
|
+
"name": "STORES_ITEM_PAYMENT_ENABLED",
|
|
1963
|
+
"kind": "const",
|
|
1964
|
+
"jsdoc": "/**\n * Whether Wix Stores items sell through the App Store, through wix-iap StartItemPayment\n * (catalyst-server #24496), which places the member's eCom order for one catalog item. OFF: that RPC\n * is not deployed, the member's identity across Serve → wix-iap → eCom is unproven, and a paid Stores\n * order is not yet proven to deliver the download. The one switch: it adds the Stores rule to\n * `APPLE_SALE_RULES`, which the mapping source and `himi store iap connect` read, and turns on the\n * checkout's path (`itemPaymentEnabled` in @himalaya/iap-checkout).\n */",
|
|
1965
|
+
"decl": "export const STORES_ITEM_PAYMENT_ENABLED = false;"
|
|
1966
|
+
},
|
|
1967
|
+
{
|
|
1968
|
+
"name": "STORES_PRODUCT_SALE_RULE",
|
|
1969
|
+
"kind": "const",
|
|
1970
|
+
"jsdoc": "/** StartItemPayment's CatalogReference: the purchases catalog sells a Stores product under its product\n * id (eCom's `catalogItemId`). One-time unlocks only, whatever the backend also accepts. */",
|
|
1971
|
+
"decl": "export const STORES_PRODUCT_SALE_RULE: AppleSaleRule = { appProductId: (id) => id, productType: \"NON_CONSUMABLE\" };"
|
|
1972
|
+
},
|
|
1961
1973
|
{
|
|
1962
1974
|
"name": "APPLE_SALE_RULES",
|
|
1963
1975
|
"kind": "const",
|
|
1964
|
-
"jsdoc": "/**\n * The one definition `himi store iap connect` writes mappings by and the mapping source reads them by,\n * keyed by catalog kind. wix-iap StartPayment maps the cashier order's single item id, and Online\n * Programs builds that order with the program id as the item id; programs are one-time unlocks.\n * Other verticals' order item ids are not established yet, so their items keep their own commerce.\n */",
|
|
1965
|
-
"decl": "export const APPLE_SALE_RULES: Readonly<Record<string, AppleSaleRule>> =
|
|
1976
|
+
"jsdoc": "/**\n * The one definition `himi store iap connect` writes mappings by and the mapping source reads them by,\n * keyed by catalog kind. wix-iap StartPayment maps the cashier order's single item id, and Online\n * Programs builds that order with the program id as the item id; programs are one-time unlocks.\n * Wix Stores joins only with `STORES_ITEM_PAYMENT_ENABLED`. Other verticals' order item ids are not\n * established yet, so their items keep their own commerce.\n */",
|
|
1977
|
+
"decl": "export const APPLE_SALE_RULES: Readonly<Record<string, AppleSaleRule>> = STORES_ITEM_PAYMENT_ENABLED\n ? { onlineProgram: ONLINE_PROGRAM_SALE_RULE, storesProduct: STORES_PRODUCT_SALE_RULE }\n : { onlineProgram: ONLINE_PROGRAM_SALE_RULE };"
|
|
1966
1978
|
},
|
|
1967
1979
|
{
|
|
1968
1980
|
"name": "HostCallExpectation",
|
|
@@ -1998,7 +2010,7 @@
|
|
|
1998
2010
|
"name": "WixIapOperation",
|
|
1999
2011
|
"kind": "type",
|
|
2000
2012
|
"jsdoc": "",
|
|
2001
|
-
"decl": "export type WixIapOperation = \"startPayment\" | \"completePayment\" | \"getPayment\";"
|
|
2013
|
+
"decl": "export type WixIapOperation = \"startPayment\" | \"startItemPayment\" | \"completePayment\" | \"getPayment\";"
|
|
2002
2014
|
},
|
|
2003
2015
|
{
|
|
2004
2016
|
"name": "StartPaymentErrorCode",
|
|
@@ -2006,6 +2018,12 @@
|
|
|
2006
2018
|
"jsdoc": "",
|
|
2007
2019
|
"decl": "export type StartPaymentErrorCode = \"NOT_PENDING\" | \"WRONG_PAYMENT_METHOD\" | \"NOT_ORDER_BUYER\" | \"UNSUPPORTED_ORDER\"\n | \"PRODUCT_NOT_AVAILABLE\" | \"ALREADY_OWNED\";"
|
|
2008
2020
|
},
|
|
2021
|
+
{
|
|
2022
|
+
"name": "StartItemPaymentErrorCode",
|
|
2023
|
+
"kind": "type",
|
|
2024
|
+
"jsdoc": "",
|
|
2025
|
+
"decl": "export type StartItemPaymentErrorCode = \"PRODUCT_NOT_AVAILABLE\" | \"ALREADY_OWNED\" | \"ITEM_NOT_AVAILABLE\"\n | \"SITE_NOT_ACCEPTING_PAYMENTS\" | \"UNSUPPORTED_ORDER\";"
|
|
2026
|
+
},
|
|
2009
2027
|
{
|
|
2010
2028
|
"name": "CompletePaymentErrorCode",
|
|
2011
2029
|
"kind": "type",
|
|
@@ -2036,6 +2054,24 @@
|
|
|
2036
2054
|
"jsdoc": "/** `appAccountToken` goes to StoreKit with the purchase. It is minted for this one payment (a\n * purchase intent, D61), not the member: StartPayment for the same Wix transaction again returns\n * the same payment and the same token. Keep it out of screen state. */",
|
|
2037
2055
|
"decl": "export type StartPaymentResult =\n | { status: \"started\"; payment: WixIapPayment; storeProductId: string; appAccountToken: string }\n | WixIapFailure<StartPaymentErrorCode>;"
|
|
2038
2056
|
},
|
|
2057
|
+
{
|
|
2058
|
+
"name": "EcomCatalogReference",
|
|
2059
|
+
"kind": "interface",
|
|
2060
|
+
"jsdoc": "/** One item a Wix catalog sells through eCom, as eCom's `catalogReference` names it: for Wix Stores,\n * the product id and the Stores app id. The purchases catalog sells it under `catalogItemId` as its\n * `appProductId`. Variants and options are not supported, and the quantity is always 1. */",
|
|
2061
|
+
"decl": "export interface EcomCatalogReference {\n catalogItemId: string;\n catalogAppId: string;\n}"
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
"name": "StartItemPaymentOutcomeUnknown",
|
|
2065
|
+
"kind": "interface",
|
|
2066
|
+
"jsdoc": "/** StartItemPayment was handed to the transport and gave no answer this client can act on: an\n * outage, a lost or late answer, or a 2xx it could not decode. The service may have placed an\n * eCom order and started a Payment for it, and it has no idempotency key, so resending places\n * another order. No token came back, so nothing can be sold against it either. */",
|
|
2067
|
+
"decl": "export interface StartItemPaymentOutcomeUnknown {\n status: \"outcomeUnknown\";\n operation: \"startItemPayment\";\n kind: WixIapFailureKind;\n httpStatus?: number;\n}"
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
"name": "StartItemPaymentResult",
|
|
2071
|
+
"kind": "type",
|
|
2072
|
+
"jsdoc": "/** StartItemPayment places the member's eCom order for one catalog item, charges it with\n * `appleIAP` and starts its Payment, all server-side. Four different ids come back: the eCom\n * order (`ecomOrderId`), the Wix Payments charge (`payment.wixTransactionId`), the wix-iap\n * Payment (`payment.paymentId`) and the StoreKit `appAccountToken`. Keep them out of screen state. */",
|
|
2073
|
+
"decl": "export type StartItemPaymentResult =\n | { status: \"started\"; payment: WixIapPayment; storeProductId: string; appAccountToken: string; ecomOrderId: string }\n | WixIapFailure<StartItemPaymentErrorCode>\n | StartItemPaymentOutcomeUnknown;"
|
|
2074
|
+
},
|
|
2039
2075
|
{
|
|
2040
2076
|
"name": "CompletePaymentResult",
|
|
2041
2077
|
"kind": "type",
|
|
@@ -2059,13 +2095,13 @@
|
|
|
2059
2095
|
"name": "WixIapClient",
|
|
2060
2096
|
"kind": "interface",
|
|
2061
2097
|
"jsdoc": "",
|
|
2062
|
-
"decl": "export interface WixIapClient {\n /** StartPayment for a pending `appleIAP` Wix Payments transaction the member placed. Returns the\n * store product to sell and this payment's `appAccountToken` to pass to StoreKit. */\n startPayment(input: { wixTransactionId: string }): Promise<StartPaymentResult>;\n /** CompletePayment with held Apple proof, sent unchanged: the service pays the payment whose\n * token the transaction carries. Success can still be `approving`. */\n completePayment(transaction: AppleTransaction): Promise<CompletePaymentResult>;\n /** GetPayment: one of the member's own Payments. */\n getPayment(input: { paymentId: string }): Promise<GetPaymentResult>;\n}",
|
|
2098
|
+
"decl": "export interface WixIapClient {\n /** StartPayment for a pending `appleIAP` Wix Payments transaction the member placed. Returns the\n * store product to sell and this payment's `appAccountToken` to pass to StoreKit. */\n startPayment(input: { wixTransactionId: string }): Promise<StartPaymentResult>;\n /** StartItemPayment for one catalog item: the service places the member's eCom order, charges it\n * and starts its Payment. Every call places a new order, so `outcomeUnknown` must never be\n * resent as if nothing happened. Returns the store product, the token and the eCom order. */\n startItemPayment(input: { item: EcomCatalogReference }): Promise<StartItemPaymentResult>;\n /** CompletePayment with held Apple proof, sent unchanged: the service pays the payment whose\n * token the transaction carries. Success can still be `approving`. */\n completePayment(transaction: AppleTransaction): Promise<CompletePaymentResult>;\n /** GetPayment: one of the member's own Payments. */\n getPayment(input: { paymentId: string }): Promise<GetPaymentResult>;\n}",
|
|
2063
2099
|
"verified": false
|
|
2064
2100
|
},
|
|
2065
2101
|
{
|
|
2066
2102
|
"name": "createWixIapClient",
|
|
2067
2103
|
"kind": "function",
|
|
2068
|
-
"jsdoc": "/**\n * Creates a client for Wix's in-app-purchase payments service (`mobile-apps-wix-iap`), which pays\n * a Wix order with a store purchase the member makes in the app.\n *\n * The shared checkout (`createIapCommerce`) builds it with Serve's IAP route for an app whose binary\n * carries a purchases-app binding; `route` stays a parameter with no default. It calls\n * StartPayment (`POST …/payments/start`), CompletePayment (`POST …/payments/complete`)
|
|
2104
|
+
"jsdoc": "/**\n * Creates a client for Wix's in-app-purchase payments service (`mobile-apps-wix-iap`), which pays\n * a Wix order with a store purchase the member makes in the app.\n *\n * The shared checkout (`createIapCommerce`) builds it with Serve's IAP route for an app whose binary\n * carries a purchases-app binding; `route` stays a parameter with no default. It calls\n * StartPayment (`POST …/payments/start`), StartItemPayment (`POST …/payments/start-item`, not yet\n * deployed), CompletePayment (`POST …/payments/complete`) and GetPayment (`GET …/payments/{id}`) through the\n * `wix_api` bridge. A matched or approved Payment is not durable access and never finishes the\n * store transaction.\n *\n * A sale needs the binding baked into the binary and an active mapping for the item; without them\n * nothing is offered, and before a production Serve release carries the IAP routes the mapping read\n * fails, so nothing is offered either. When this client uses the host\n * bridge, it also requires the host's identity clock and passes that captured expectation\n * through the bridge. An injected transport keeps the previous call shape unless the session\n * itself exposes an identity.\n */",
|
|
2069
2105
|
"decl": "export function createWixIapClient(deps: WixIapClientDependencies): WixIapClient;",
|
|
2070
2106
|
"verified": false
|
|
2071
2107
|
},
|
|
@@ -2298,6 +2334,12 @@
|
|
|
2298
2334
|
"jsdoc": "",
|
|
2299
2335
|
"decl": "export { createWorkerOrderRefStore } from \"./order-refs.js\";"
|
|
2300
2336
|
},
|
|
2337
|
+
{
|
|
2338
|
+
"name": "\"./checkout-source.js\"",
|
|
2339
|
+
"kind": "reexport",
|
|
2340
|
+
"jsdoc": "",
|
|
2341
|
+
"decl": "export { itemPaymentEnabled } from \"./checkout-source.js\";"
|
|
2342
|
+
},
|
|
2301
2343
|
{
|
|
2302
2344
|
"name": "\"./apple-iap-contract.js\"",
|
|
2303
2345
|
"kind": "reexport",
|
|
@@ -2334,6 +2376,12 @@
|
|
|
2334
2376
|
"jsdoc": "/** Persists unfinished orders per app and member, using the host KV unless storage is supplied. */",
|
|
2335
2377
|
"decl": "export function createWorkerOrderRefStore(storage?: KeyValueStorage): OrderRefStore;"
|
|
2336
2378
|
},
|
|
2379
|
+
{
|
|
2380
|
+
"name": "itemPaymentEnabled",
|
|
2381
|
+
"kind": "function",
|
|
2382
|
+
"jsdoc": "/**\n * Whether an item whose adapter names it in eCom's catalog (a Wix Stores product) is paid through\n * wix-iap StartItemPayment, which places the member's eCom order server-side. The same switch as\n * the Stores sale rule (`STORES_ITEM_PAYMENT_ENABLED`, @himalaya/iap), so Stores items reach an App\n * Store route only when this path sells them. Turning it off again stops only new attempts: stored\n * ones are still held, completed and tracked ().\n */",
|
|
2383
|
+
"decl": "export function itemPaymentEnabled(): boolean;"
|
|
2384
|
+
},
|
|
2337
2385
|
{
|
|
2338
2386
|
"name": "WixBridge",
|
|
2339
2387
|
"kind": "interface",
|
|
@@ -2398,7 +2446,7 @@
|
|
|
2398
2446
|
"name": "IapOrderAdapter",
|
|
2399
2447
|
"kind": "interface",
|
|
2400
2448
|
"jsdoc": "/** The only per-vertical code. Every call goes through the session-bound bridge it is given. */",
|
|
2401
|
-
"decl": "export interface IapOrderAdapter {\n /** The catalog kind it sells (`onlineProgram`, `pricingPlan`, `storesProduct`, or a custom one). */\n readonly kind: string;\n /** Structural check before any call: one item, quantity 1, a supported shape. */\n eligibility(item: IapOrderItem): IapEligibility;\n /** Opens, or reuses the vertical's own open, order for the signed-in member. */\n openOrder(item: IapOrderItem, bridge: SessionBoundBridge): Promise<OrderAttempt>;\n /** Re-reads an order opened earlier; `open` only while it can still be paid. */\n reopenOrder(order: { cashierOrderId: string; verticalRef: VerticalRef }, bridge: SessionBoundBridge): Promise<OrderAttempt>;\n /** The vertical's authoritative answer for the order's item and the signed-in member. */\n readAccess(verticalRef: VerticalRef, bridge: SessionBoundBridge): Promise<AccessRead>;\n /** The member's access to an item before any order exists (bought through Wix, say). Read-only:\n * never opens or prepares an order. An adapter without it shows Get access until a sale says otherwise. */\n currentAccess?(item: IapOrderItem, bridge: SessionBoundBridge): Promise<AccessRead>;\n}"
|
|
2449
|
+
"decl": "export interface IapOrderAdapter {\n /** The catalog kind it sells (`onlineProgram`, `pricingPlan`, `storesProduct`, or a custom one). */\n readonly kind: string;\n /** Structural check before any call: one item, quantity 1, a supported shape. */\n eligibility(item: IapOrderItem): IapEligibility;\n /** Opens, or reuses the vertical's own open, order for the signed-in member. */\n openOrder(item: IapOrderItem, bridge: SessionBoundBridge): Promise<OrderAttempt>;\n /** Re-reads an order opened earlier; `open` only while it can still be paid. */\n reopenOrder(order: { cashierOrderId: string; verticalRef: VerticalRef }, bridge: SessionBoundBridge): Promise<OrderAttempt>;\n /** The vertical's authoritative answer for the order's item and the signed-in member. */\n readAccess(verticalRef: VerticalRef, bridge: SessionBoundBridge): Promise<AccessRead>;\n /** The member's access to an item before any order exists (bought through Wix, say). Read-only:\n * never opens or prepares an order. An adapter without it shows Get access until a sale says otherwise. */\n currentAccess?(item: IapOrderItem, bridge: SessionBoundBridge): Promise<AccessRead>;\n /** When the platform enables StartItemPayment, an eligible item whose order wix-iap places itself\n * instead of `openOrder`. Null for an item outside that shape, which keeps `openOrder`. */\n itemPayment?(item: IapOrderItem): ItemPayment | null;\n}"
|
|
2402
2450
|
},
|
|
2403
2451
|
{
|
|
2404
2452
|
"name": "OrderPartitionKey",
|
|
@@ -2409,8 +2457,8 @@
|
|
|
2409
2457
|
{
|
|
2410
2458
|
"name": "OrderRefStage",
|
|
2411
2459
|
"kind": "type",
|
|
2412
|
-
"jsdoc": "/** `held`: Apple charged, and CompletePayment refused the proof without recording it, so the
|
|
2413
|
-
"decl": "export type OrderRefStage = \"ordered\" | \"charged\" | \"started\" | \"submitted\" | \"held\" | \"needsSupport\";"
|
|
2460
|
+
"jsdoc": "/** `startingItem`: a StartItemPayment was or is about to be sent and its answer is not stored, so\n * the service may have placed an order this device knows nothing of; nothing else is sold for the\n * item until it expires. `held`: Apple charged, and CompletePayment refused the proof without\n * recording it, so the transaction stays unfinished and is retried. `needsSupport`: recorded and\n * refused for good. */",
|
|
2461
|
+
"decl": "export type OrderRefStage = \"startingItem\" | \"ordered\" | \"charged\" | \"started\" | \"submitted\" | \"held\" | \"needsSupport\";"
|
|
2414
2462
|
},
|
|
2415
2463
|
{
|
|
2416
2464
|
"name": "OrderRef",
|
|
@@ -2434,7 +2482,7 @@
|
|
|
2434
2482
|
"name": "OrderFailureCode",
|
|
2435
2483
|
"kind": "type",
|
|
2436
2484
|
"jsdoc": "",
|
|
2437
|
-
"decl": "export type OrderFailureCode =\n | \"notEnabled\" | \"notConfigured\" | \"signInRequired\" | \"unsupportedItem\" | \"notAppleRoute\"\n | \"storageUnavailable\" | \"approvalRequired\" | \"orderClosed\" | \"orderRefused\" | \"chargeFailed\"\n | \"paymentRefused\" | \"paymentHeld\" | \"alreadyOwned\" | \"paymentInProgress\" | \"productChanged\"\n | \"invalidAssociation\" | \"needsSupport\" | \"unavailable\" | \"sessionChanged\";"
|
|
2485
|
+
"decl": "export type OrderFailureCode =\n | \"notEnabled\" | \"notConfigured\" | \"signInRequired\" | \"unsupportedItem\" | \"notAppleRoute\"\n | \"storageUnavailable\" | \"approvalRequired\" | \"orderClosed\" | \"orderRefused\" | \"chargeFailed\"\n | \"paymentRefused\" | \"paymentHeld\" | \"alreadyOwned\" | \"paymentInProgress\" | \"productChanged\"\n | \"invalidAssociation\" | \"needsSupport\" | \"unavailable\" | \"sessionChanged\" | \"outcomeUnknown\";"
|
|
2438
2486
|
},
|
|
2439
2487
|
{
|
|
2440
2488
|
"name": "PaymentAccess",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1369.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"artifactId": "himalaya-cli"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
|
-
"falconPackageHash": "
|
|
74
|
+
"falconPackageHash": "fd3751d6f6d280437ca5e65dad0bcfc33119ae43eff7ee5ad3523d30"
|
|
75
75
|
}
|
|
@@ -26717,7 +26717,55 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
26717
26717
|
"modifiers": {
|
|
26718
26718
|
"cornerRadius": 8,
|
|
26719
26719
|
"pressFeedback": "scale",
|
|
26720
|
-
"haptic": "medium"
|
|
26720
|
+
"haptic": "medium",
|
|
26721
|
+
"hiddenPath": "$service:himiStoreProduct.cartHidden"
|
|
26722
|
+
}
|
|
26723
|
+
},
|
|
26724
|
+
{
|
|
26725
|
+
"id": "himi_product.buy",
|
|
26726
|
+
"body": {
|
|
26727
|
+
"package": "himalaya",
|
|
26728
|
+
"type": "Button",
|
|
26729
|
+
"props": {
|
|
26730
|
+
"title": "$service:himiStoreProduct.buyLabel",
|
|
26731
|
+
"style": "primary",
|
|
26732
|
+
"fullWidth": true,
|
|
26733
|
+
"disabledPath": "$service:himiStoreProduct.buyDisabled",
|
|
26734
|
+
"action": {
|
|
26735
|
+
"actionId": "tier5.himiStoreProduct.buy"
|
|
26736
|
+
}
|
|
26737
|
+
}
|
|
26738
|
+
},
|
|
26739
|
+
"modifiers": {
|
|
26740
|
+
"hiddenPath": "$service:himiStoreProduct.buyHidden"
|
|
26741
|
+
}
|
|
26742
|
+
},
|
|
26743
|
+
{
|
|
26744
|
+
"id": "himi_product.buy_note",
|
|
26745
|
+
"body": {
|
|
26746
|
+
"package": "himalaya",
|
|
26747
|
+
"type": "Text",
|
|
26748
|
+
"props": {
|
|
26749
|
+
"text": "$service:himiStoreProduct.buyNote",
|
|
26750
|
+
"style": "footnote",
|
|
26751
|
+
"color": "textSecondary",
|
|
26752
|
+
"collapseWhenEmpty": true
|
|
26753
|
+
}
|
|
26754
|
+
},
|
|
26755
|
+
"modifiers": {
|
|
26756
|
+
"hiddenPath": "$service:himiStoreProduct.buyHidden"
|
|
26757
|
+
}
|
|
26758
|
+
},
|
|
26759
|
+
{
|
|
26760
|
+
"id": "himi_product.purchased",
|
|
26761
|
+
"body": {
|
|
26762
|
+
"package": "himalaya",
|
|
26763
|
+
"type": "Text",
|
|
26764
|
+
"props": {
|
|
26765
|
+
"text": "$service:himiStoreProduct.purchasedNote",
|
|
26766
|
+
"style": "footnote",
|
|
26767
|
+
"collapseWhenEmpty": true
|
|
26768
|
+
}
|
|
26721
26769
|
}
|
|
26722
26770
|
},
|
|
26723
26771
|
{
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { defineService, type ServiceDefinition } from "@himalaya/state";
|
|
2
|
-
import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
|
|
2
|
+
import { createIapCommerce, type IapCommerce, type IapOrderAdapter } from "@himalaya/iap-checkout";
|
|
3
3
|
import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
|
|
4
4
|
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
|
|
5
5
|
|
|
6
6
|
// Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
|
|
7
7
|
export function withIapPresentation<S extends object>(options: {
|
|
8
8
|
register?: typeof defineService;
|
|
9
|
+
/** The verticals this screen sells; none for a screen that only shows prices. */
|
|
10
|
+
adapters?: readonly IapOrderAdapter[];
|
|
9
11
|
onCreate?(commerce: IapCommerce): void;
|
|
10
12
|
onContent?(state: S): void;
|
|
11
13
|
items(state: S): IapCatalogItem[];
|
|
@@ -15,7 +17,7 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
15
17
|
prefix: string;
|
|
16
18
|
}): typeof defineService {
|
|
17
19
|
return ((name: string, definition: ServiceDefinition<S>) => {
|
|
18
|
-
const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
|
|
20
|
+
const commerce = createIapCommerce({ adapters: options.adapters, onChange: () => { void service.iapPublish(); } });
|
|
19
21
|
options.onCreate?.(commerce);
|
|
20
22
|
let items: IapCatalogItem[] = [];
|
|
21
23
|
let contentVersion = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Tier-5 bundle: himi-store — product detail.
|
|
2
2
|
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
3
|
-
import { type IapCommerce } from "@himalaya/iap-checkout";
|
|
4
|
-
import { iapPriceCopy } from "../_shared/iap-worker.js";
|
|
3
|
+
import { itemPaymentEnabled, storesProductOrders, type IapCommerce, type IapPurchaseFields } from "@himalaya/iap-checkout";
|
|
4
|
+
import { iapPriceCopy, iapPurchaseCopy } from "../_shared/iap-worker.js";
|
|
5
5
|
import { share as shareSheet } from "@himalaya/share";
|
|
6
6
|
import { addToCurrentCart, getCurrentCart } from "@himalaya/wix-ecom";
|
|
7
7
|
import { get, upsert, enqueue, type DbEntity } from "@himalaya/local-db";
|
|
@@ -202,6 +202,14 @@ interface S {
|
|
|
202
202
|
opt1Label: string; opt1Chips: OptionChip[]; opt1Selected: string[]; noOpt1: boolean;
|
|
203
203
|
opt2Label: string; opt2Chips: OptionChip[]; opt2Selected: string[]; noOpt2: boolean;
|
|
204
204
|
opt3Label: string; opt3Chips: OptionChip[]; opt3Selected: string[]; noOpt3: boolean;
|
|
205
|
+
/** The App Store action (`commerce.purchaseFields`), in place of Add to cart while it shows. */
|
|
206
|
+
cartHidden: boolean;
|
|
207
|
+
buyHidden: boolean;
|
|
208
|
+
buyLabel: string;
|
|
209
|
+
buyDisabled: boolean;
|
|
210
|
+
buyNote: string;
|
|
211
|
+
/** Shown once the eCom order reads paid, never on the payment's approval alone. */
|
|
212
|
+
purchasedNote: string;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
215
|
function inputId(): string {
|
|
@@ -309,14 +317,53 @@ function derive(s: S): S {
|
|
|
309
317
|
// read answering late — must not replace the badge the add set (#3468 review).
|
|
310
318
|
let cartBadgeEpoch = 0;
|
|
311
319
|
|
|
320
|
+
// A digital product with no options sells through the App Store once the platform pays for Wix
|
|
321
|
+
// Stores items with wix-iap StartItemPayment (`itemPaymentEnabled`). Until then, and for any other
|
|
322
|
+
// product, an App Store route only blocks the regular checkout, as before.
|
|
323
|
+
const SELLS_THROUGH_APPLE = itemPaymentEnabled();
|
|
324
|
+
const WIX_STORES_APP = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
|
|
325
|
+
const NOT_APPLE: IapPurchaseFields = { appleRoute: false, accessReady: false, buyLabel: "", buyDisabled: true, buyBusy: false, buyNote: "" };
|
|
326
|
+
|
|
312
327
|
let productPricing: IapCommerce;
|
|
328
|
+
let checkoutEntered = false;
|
|
313
329
|
const defineService = withIapPresentation<S>({
|
|
314
|
-
|
|
330
|
+
adapters: SELLS_THROUGH_APPLE ? [storesProductOrders] : [],
|
|
331
|
+
onCreate: commerce => { productPricing = commerce; }, prefix: "product",
|
|
332
|
+
transientKeys: ["price", "optionsIncomplete", "cartHint", "cartHidden", "buyHidden", "buyLabel", "buyDisabled", "buyNote", "purchasedNote"], contentActions: ["load", "paintCached"],
|
|
315
333
|
items: state => inputId() ? [{ reference: { kind: "storesProduct", id: inputId() }, commerceDisplayPrice: state.price }] : [],
|
|
316
|
-
present: (state, price, allowed) =>
|
|
317
|
-
|
|
334
|
+
present: (state, price, allowed) => {
|
|
335
|
+
const purchase = applePurchase(state);
|
|
336
|
+
const unavailable = iapPriceCopy("product").purchaseUnavailable;
|
|
337
|
+
const blocked = lastProduct?.productType?.toLowerCase() === "digital" || (!state.optionsUnknown && !allowed(inputId()));
|
|
338
|
+
return {
|
|
339
|
+
...state, price: price(inputId()), optionsIncomplete: derive(state).optionsIncomplete || !allowed(inputId()) || lastProduct?.productType?.toLowerCase() === "digital",
|
|
340
|
+
cartHint: !purchase.appleRoute && blocked ? unavailable : state.cartHint === unavailable ? "" : state.cartHint,
|
|
341
|
+
cartHidden: purchase.appleRoute,
|
|
342
|
+
buyHidden: !purchase.appleRoute || purchase.accessReady, buyLabel: purchase.buyLabel, buyDisabled: purchase.buyDisabled, buyNote: purchase.buyNote,
|
|
343
|
+
purchasedNote: purchase.accessReady ? copyParam("product.purchasedText", "Purchased") : "",
|
|
344
|
+
};
|
|
345
|
+
},
|
|
318
346
|
});
|
|
319
347
|
|
|
348
|
+
/** The App Store action, only for the one shape it sells: digital, with no options to choose, read
|
|
349
|
+
* from the product loaded for the id on screen (as Add to cart is). A cached product whose
|
|
350
|
+
* options were never read is not known to have none. */
|
|
351
|
+
function applePurchase(state: S): IapPurchaseFields {
|
|
352
|
+
const id = inputId();
|
|
353
|
+
const loaded = !!id && lastLoadedId === id;
|
|
354
|
+
const sellable = SELLS_THROUGH_APPLE && loaded && lastProduct?.productType?.toLowerCase() === "digital" && !state.optionsUnknown && state.optionGroups.length === 0;
|
|
355
|
+
const copy = { ...iapPurchaseCopy("product"), buy: copyParam("product.buyCta", "Buy") };
|
|
356
|
+
const fields = sellable ? productPricing.purchaseFields({ kind: "storesProduct", id }, copy) : NOT_APPLE;
|
|
357
|
+
return fields.appleRoute ? fields : NOT_APPLE;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** On every entry: the first starts the checkout (recovering held purchases), later ones reconcile. */
|
|
361
|
+
function enterCheckout(): void {
|
|
362
|
+
if (!SELLS_THROUGH_APPLE) return;
|
|
363
|
+
void (checkoutEntered ? productPricing.reconcile() : productPricing.start());
|
|
364
|
+
checkoutEntered = true;
|
|
365
|
+
}
|
|
366
|
+
|
|
320
367
|
const svc = defineService<S, {
|
|
321
368
|
load: (s: S, arg?: unknown) => Promise<S>;
|
|
322
369
|
setQuantity: (s: S, arg?: unknown) => S;
|
|
@@ -329,6 +376,8 @@ const svc = defineService<S, {
|
|
|
329
376
|
toggleSave: (s: S) => S;
|
|
330
377
|
/** [rico] #3372 — RichContent's on_link: `{ url }` of the tapped link. */
|
|
331
378
|
openLink: (s: S, arg?: unknown) => Promise<S>;
|
|
379
|
+
/** The App Store Buy: through the shared checkout, for the product on screen. */
|
|
380
|
+
buy: (s: S) => Promise<S>;
|
|
332
381
|
}>("himiStoreProduct", {
|
|
333
382
|
state: derive({
|
|
334
383
|
status: "loading",
|
|
@@ -375,10 +424,17 @@ const svc = defineService<S, {
|
|
|
375
424
|
opt1Label: "", opt1Chips: [], opt1Selected: [], noOpt1: true,
|
|
376
425
|
opt2Label: "", opt2Chips: [], opt2Selected: [], noOpt2: true,
|
|
377
426
|
opt3Label: "", opt3Chips: [], opt3Selected: [], noOpt3: true,
|
|
427
|
+
cartHidden: false,
|
|
428
|
+
buyHidden: true,
|
|
429
|
+
buyLabel: "",
|
|
430
|
+
buyDisabled: true,
|
|
431
|
+
buyNote: "",
|
|
432
|
+
purchasedNote: "",
|
|
378
433
|
}),
|
|
379
434
|
actions: {
|
|
380
435
|
load: async (s, arg): Promise<S> => {
|
|
381
436
|
stashCopy(arg);
|
|
437
|
+
enterCheckout();
|
|
382
438
|
const sequence = ++productLoadSequence;
|
|
383
439
|
lastProduct = undefined;
|
|
384
440
|
const id = inputId();
|
|
@@ -657,6 +713,13 @@ const svc = defineService<S, {
|
|
|
657
713
|
openLink: async (s, arg) => ((await openRichLink(arg))
|
|
658
714
|
? s
|
|
659
715
|
: derive({ ...s, added: false, cartHint: copyParam("product.linkOpenFailed", "This link could not be opened.") })),
|
|
716
|
+
buy: async (s): Promise<S> => {
|
|
717
|
+
const id = inputId();
|
|
718
|
+
const purchase = applePurchase(s);
|
|
719
|
+
if (!purchase.appleRoute || purchase.buyDisabled) return s;
|
|
720
|
+
await productPricing.buy({ reference: { kind: "storesProduct", id }, catalogReference: { appId: WIX_STORES_APP, catalogItemId: id }, quantity: 1 });
|
|
721
|
+
return svc.getState() as S;
|
|
722
|
+
},
|
|
660
723
|
},
|
|
661
724
|
});
|
|
662
725
|
|
|
@@ -82885,7 +82885,55 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
82885
82885
|
"modifiers": {
|
|
82886
82886
|
"cornerRadius": 8,
|
|
82887
82887
|
"pressFeedback": "scale",
|
|
82888
|
-
"haptic": "medium"
|
|
82888
|
+
"haptic": "medium",
|
|
82889
|
+
"hiddenPath": "$service:himiStoreProduct.cartHidden"
|
|
82890
|
+
}
|
|
82891
|
+
},
|
|
82892
|
+
{
|
|
82893
|
+
"id": "himi_product.buy",
|
|
82894
|
+
"body": {
|
|
82895
|
+
"package": "himalaya",
|
|
82896
|
+
"type": "Button",
|
|
82897
|
+
"props": {
|
|
82898
|
+
"title": "$service:himiStoreProduct.buyLabel",
|
|
82899
|
+
"style": "primary",
|
|
82900
|
+
"fullWidth": true,
|
|
82901
|
+
"disabledPath": "$service:himiStoreProduct.buyDisabled",
|
|
82902
|
+
"action": {
|
|
82903
|
+
"actionId": "tier5.himiStoreProduct.buy"
|
|
82904
|
+
}
|
|
82905
|
+
}
|
|
82906
|
+
},
|
|
82907
|
+
"modifiers": {
|
|
82908
|
+
"hiddenPath": "$service:himiStoreProduct.buyHidden"
|
|
82909
|
+
}
|
|
82910
|
+
},
|
|
82911
|
+
{
|
|
82912
|
+
"id": "himi_product.buy_note",
|
|
82913
|
+
"body": {
|
|
82914
|
+
"package": "himalaya",
|
|
82915
|
+
"type": "Text",
|
|
82916
|
+
"props": {
|
|
82917
|
+
"text": "$service:himiStoreProduct.buyNote",
|
|
82918
|
+
"style": "footnote",
|
|
82919
|
+
"color": "textSecondary",
|
|
82920
|
+
"collapseWhenEmpty": true
|
|
82921
|
+
}
|
|
82922
|
+
},
|
|
82923
|
+
"modifiers": {
|
|
82924
|
+
"hiddenPath": "$service:himiStoreProduct.buyHidden"
|
|
82925
|
+
}
|
|
82926
|
+
},
|
|
82927
|
+
{
|
|
82928
|
+
"id": "himi_product.purchased",
|
|
82929
|
+
"body": {
|
|
82930
|
+
"package": "himalaya",
|
|
82931
|
+
"type": "Text",
|
|
82932
|
+
"props": {
|
|
82933
|
+
"text": "$service:himiStoreProduct.purchasedNote",
|
|
82934
|
+
"style": "footnote",
|
|
82935
|
+
"collapseWhenEmpty": true
|
|
82936
|
+
}
|
|
82889
82937
|
}
|
|
82890
82938
|
},
|
|
82891
82939
|
{
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { defineService, type ServiceDefinition } from "@himalaya/state";
|
|
2
|
-
import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
|
|
2
|
+
import { createIapCommerce, type IapCommerce, type IapOrderAdapter } from "@himalaya/iap-checkout";
|
|
3
3
|
import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
|
|
4
4
|
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
|
|
5
5
|
|
|
6
6
|
// Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
|
|
7
7
|
export function withIapPresentation<S extends object>(options: {
|
|
8
8
|
register?: typeof defineService;
|
|
9
|
+
/** The verticals this screen sells; none for a screen that only shows prices. */
|
|
10
|
+
adapters?: readonly IapOrderAdapter[];
|
|
9
11
|
onCreate?(commerce: IapCommerce): void;
|
|
10
12
|
onContent?(state: S): void;
|
|
11
13
|
items(state: S): IapCatalogItem[];
|
|
@@ -15,7 +17,7 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
15
17
|
prefix: string;
|
|
16
18
|
}): typeof defineService {
|
|
17
19
|
return ((name: string, definition: ServiceDefinition<S>) => {
|
|
18
|
-
const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
|
|
20
|
+
const commerce = createIapCommerce({ adapters: options.adapters, onChange: () => { void service.iapPublish(); } });
|
|
19
21
|
options.onCreate?.(commerce);
|
|
20
22
|
let items: IapCatalogItem[] = [];
|
|
21
23
|
let contentVersion = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Tier-5 bundle: himi-store — product detail.
|
|
2
2
|
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
3
|
-
import { type IapCommerce } from "@himalaya/iap-checkout";
|
|
4
|
-
import { iapPriceCopy } from "../_shared/iap-worker.js";
|
|
3
|
+
import { itemPaymentEnabled, storesProductOrders, type IapCommerce, type IapPurchaseFields } from "@himalaya/iap-checkout";
|
|
4
|
+
import { iapPriceCopy, iapPurchaseCopy } from "../_shared/iap-worker.js";
|
|
5
5
|
import { share as shareSheet } from "@himalaya/share";
|
|
6
6
|
import { addToCurrentCart, getCurrentCart } from "@himalaya/wix-ecom";
|
|
7
7
|
import { get, upsert, enqueue, type DbEntity } from "@himalaya/local-db";
|
|
@@ -202,6 +202,14 @@ interface S {
|
|
|
202
202
|
opt1Label: string; opt1Chips: OptionChip[]; opt1Selected: string[]; noOpt1: boolean;
|
|
203
203
|
opt2Label: string; opt2Chips: OptionChip[]; opt2Selected: string[]; noOpt2: boolean;
|
|
204
204
|
opt3Label: string; opt3Chips: OptionChip[]; opt3Selected: string[]; noOpt3: boolean;
|
|
205
|
+
/** The App Store action (`commerce.purchaseFields`), in place of Add to cart while it shows. */
|
|
206
|
+
cartHidden: boolean;
|
|
207
|
+
buyHidden: boolean;
|
|
208
|
+
buyLabel: string;
|
|
209
|
+
buyDisabled: boolean;
|
|
210
|
+
buyNote: string;
|
|
211
|
+
/** Shown once the eCom order reads paid, never on the payment's approval alone. */
|
|
212
|
+
purchasedNote: string;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
215
|
function inputId(): string {
|
|
@@ -309,14 +317,53 @@ function derive(s: S): S {
|
|
|
309
317
|
// read answering late — must not replace the badge the add set (#3468 review).
|
|
310
318
|
let cartBadgeEpoch = 0;
|
|
311
319
|
|
|
320
|
+
// A digital product with no options sells through the App Store once the platform pays for Wix
|
|
321
|
+
// Stores items with wix-iap StartItemPayment (`itemPaymentEnabled`). Until then, and for any other
|
|
322
|
+
// product, an App Store route only blocks the regular checkout, as before.
|
|
323
|
+
const SELLS_THROUGH_APPLE = itemPaymentEnabled();
|
|
324
|
+
const WIX_STORES_APP = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
|
|
325
|
+
const NOT_APPLE: IapPurchaseFields = { appleRoute: false, accessReady: false, buyLabel: "", buyDisabled: true, buyBusy: false, buyNote: "" };
|
|
326
|
+
|
|
312
327
|
let productPricing: IapCommerce;
|
|
328
|
+
let checkoutEntered = false;
|
|
313
329
|
const defineService = withIapPresentation<S>({
|
|
314
|
-
|
|
330
|
+
adapters: SELLS_THROUGH_APPLE ? [storesProductOrders] : [],
|
|
331
|
+
onCreate: commerce => { productPricing = commerce; }, prefix: "product",
|
|
332
|
+
transientKeys: ["price", "optionsIncomplete", "cartHint", "cartHidden", "buyHidden", "buyLabel", "buyDisabled", "buyNote", "purchasedNote"], contentActions: ["load", "paintCached"],
|
|
315
333
|
items: state => inputId() ? [{ reference: { kind: "storesProduct", id: inputId() }, commerceDisplayPrice: state.price }] : [],
|
|
316
|
-
present: (state, price, allowed) =>
|
|
317
|
-
|
|
334
|
+
present: (state, price, allowed) => {
|
|
335
|
+
const purchase = applePurchase(state);
|
|
336
|
+
const unavailable = iapPriceCopy("product").purchaseUnavailable;
|
|
337
|
+
const blocked = lastProduct?.productType?.toLowerCase() === "digital" || (!state.optionsUnknown && !allowed(inputId()));
|
|
338
|
+
return {
|
|
339
|
+
...state, price: price(inputId()), optionsIncomplete: derive(state).optionsIncomplete || !allowed(inputId()) || lastProduct?.productType?.toLowerCase() === "digital",
|
|
340
|
+
cartHint: !purchase.appleRoute && blocked ? unavailable : state.cartHint === unavailable ? "" : state.cartHint,
|
|
341
|
+
cartHidden: purchase.appleRoute,
|
|
342
|
+
buyHidden: !purchase.appleRoute || purchase.accessReady, buyLabel: purchase.buyLabel, buyDisabled: purchase.buyDisabled, buyNote: purchase.buyNote,
|
|
343
|
+
purchasedNote: purchase.accessReady ? copyParam("product.purchasedText", "Purchased") : "",
|
|
344
|
+
};
|
|
345
|
+
},
|
|
318
346
|
});
|
|
319
347
|
|
|
348
|
+
/** The App Store action, only for the one shape it sells: digital, with no options to choose, read
|
|
349
|
+
* from the product loaded for the id on screen (as Add to cart is). A cached product whose
|
|
350
|
+
* options were never read is not known to have none. */
|
|
351
|
+
function applePurchase(state: S): IapPurchaseFields {
|
|
352
|
+
const id = inputId();
|
|
353
|
+
const loaded = !!id && lastLoadedId === id;
|
|
354
|
+
const sellable = SELLS_THROUGH_APPLE && loaded && lastProduct?.productType?.toLowerCase() === "digital" && !state.optionsUnknown && state.optionGroups.length === 0;
|
|
355
|
+
const copy = { ...iapPurchaseCopy("product"), buy: copyParam("product.buyCta", "Buy") };
|
|
356
|
+
const fields = sellable ? productPricing.purchaseFields({ kind: "storesProduct", id }, copy) : NOT_APPLE;
|
|
357
|
+
return fields.appleRoute ? fields : NOT_APPLE;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** On every entry: the first starts the checkout (recovering held purchases), later ones reconcile. */
|
|
361
|
+
function enterCheckout(): void {
|
|
362
|
+
if (!SELLS_THROUGH_APPLE) return;
|
|
363
|
+
void (checkoutEntered ? productPricing.reconcile() : productPricing.start());
|
|
364
|
+
checkoutEntered = true;
|
|
365
|
+
}
|
|
366
|
+
|
|
320
367
|
const svc = defineService<S, {
|
|
321
368
|
load: (s: S, arg?: unknown) => Promise<S>;
|
|
322
369
|
setQuantity: (s: S, arg?: unknown) => S;
|
|
@@ -329,6 +376,8 @@ const svc = defineService<S, {
|
|
|
329
376
|
toggleSave: (s: S) => S;
|
|
330
377
|
/** [rico] #3372 — RichContent's on_link: `{ url }` of the tapped link. */
|
|
331
378
|
openLink: (s: S, arg?: unknown) => Promise<S>;
|
|
379
|
+
/** The App Store Buy: through the shared checkout, for the product on screen. */
|
|
380
|
+
buy: (s: S) => Promise<S>;
|
|
332
381
|
}>("himiStoreProduct", {
|
|
333
382
|
state: derive({
|
|
334
383
|
status: "loading",
|
|
@@ -375,10 +424,17 @@ const svc = defineService<S, {
|
|
|
375
424
|
opt1Label: "", opt1Chips: [], opt1Selected: [], noOpt1: true,
|
|
376
425
|
opt2Label: "", opt2Chips: [], opt2Selected: [], noOpt2: true,
|
|
377
426
|
opt3Label: "", opt3Chips: [], opt3Selected: [], noOpt3: true,
|
|
427
|
+
cartHidden: false,
|
|
428
|
+
buyHidden: true,
|
|
429
|
+
buyLabel: "",
|
|
430
|
+
buyDisabled: true,
|
|
431
|
+
buyNote: "",
|
|
432
|
+
purchasedNote: "",
|
|
378
433
|
}),
|
|
379
434
|
actions: {
|
|
380
435
|
load: async (s, arg): Promise<S> => {
|
|
381
436
|
stashCopy(arg);
|
|
437
|
+
enterCheckout();
|
|
382
438
|
const sequence = ++productLoadSequence;
|
|
383
439
|
lastProduct = undefined;
|
|
384
440
|
const id = inputId();
|
|
@@ -657,6 +713,13 @@ const svc = defineService<S, {
|
|
|
657
713
|
openLink: async (s, arg) => ((await openRichLink(arg))
|
|
658
714
|
? s
|
|
659
715
|
: derive({ ...s, added: false, cartHint: copyParam("product.linkOpenFailed", "This link could not be opened.") })),
|
|
716
|
+
buy: async (s): Promise<S> => {
|
|
717
|
+
const id = inputId();
|
|
718
|
+
const purchase = applePurchase(s);
|
|
719
|
+
if (!purchase.appleRoute || purchase.buyDisabled) return s;
|
|
720
|
+
await productPricing.buy({ reference: { kind: "storesProduct", id }, catalogReference: { appId: WIX_STORES_APP, catalogItemId: id }, quantity: 1 });
|
|
721
|
+
return svc.getState() as S;
|
|
722
|
+
},
|
|
660
723
|
},
|
|
661
724
|
});
|
|
662
725
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { defineService, type ServiceDefinition } from "@himalaya/state";
|
|
2
|
-
import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
|
|
2
|
+
import { createIapCommerce, type IapCommerce, type IapOrderAdapter } from "@himalaya/iap-checkout";
|
|
3
3
|
import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
|
|
4
4
|
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
|
|
5
5
|
|
|
6
6
|
// Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
|
|
7
7
|
export function withIapPresentation<S extends object>(options: {
|
|
8
8
|
register?: typeof defineService;
|
|
9
|
+
/** The verticals this screen sells; none for a screen that only shows prices. */
|
|
10
|
+
adapters?: readonly IapOrderAdapter[];
|
|
9
11
|
onCreate?(commerce: IapCommerce): void;
|
|
10
12
|
onContent?(state: S): void;
|
|
11
13
|
items(state: S): IapCatalogItem[];
|
|
@@ -15,7 +17,7 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
15
17
|
prefix: string;
|
|
16
18
|
}): typeof defineService {
|
|
17
19
|
return ((name: string, definition: ServiceDefinition<S>) => {
|
|
18
|
-
const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
|
|
20
|
+
const commerce = createIapCommerce({ adapters: options.adapters, onChange: () => { void service.iapPublish(); } });
|
|
19
21
|
options.onCreate?.(commerce);
|
|
20
22
|
let items: IapCatalogItem[] = [];
|
|
21
23
|
let contentVersion = 0;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { defineService, type ServiceDefinition } from "@himalaya/state";
|
|
2
|
-
import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
|
|
2
|
+
import { createIapCommerce, type IapCommerce, type IapOrderAdapter } from "@himalaya/iap-checkout";
|
|
3
3
|
import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
|
|
4
4
|
import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
|
|
5
5
|
|
|
6
6
|
// Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
|
|
7
7
|
export function withIapPresentation<S extends object>(options: {
|
|
8
8
|
register?: typeof defineService;
|
|
9
|
+
/** The verticals this screen sells; none for a screen that only shows prices. */
|
|
10
|
+
adapters?: readonly IapOrderAdapter[];
|
|
9
11
|
onCreate?(commerce: IapCommerce): void;
|
|
10
12
|
onContent?(state: S): void;
|
|
11
13
|
items(state: S): IapCatalogItem[];
|
|
@@ -15,7 +17,7 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
15
17
|
prefix: string;
|
|
16
18
|
}): typeof defineService {
|
|
17
19
|
return ((name: string, definition: ServiceDefinition<S>) => {
|
|
18
|
-
const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
|
|
20
|
+
const commerce = createIapCommerce({ adapters: options.adapters, onChange: () => { void service.iapPublish(); } });
|
|
19
21
|
options.onCreate?.(commerce);
|
|
20
22
|
let items: IapCatalogItem[] = [];
|
|
21
23
|
let contentVersion = 0;
|