@wix/himalaya-cli 0.1309.0 → 0.1311.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/delivery-contract.d.ts +3 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/delivery.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/host-session.d.ts +31 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-contract.d.ts +39 -15
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-wire.d.ts +2 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments.d.ts +11 -12
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/define-app.d.ts +13 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/apple-iap-contract.d.ts +17 -9
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/iap-order-backend.d.ts +6 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +119 -65
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +125 -35
- package/dist/cli.mjs +1286 -677
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/himi-json-output.md +29 -1
- package/guides/iap-products.md +51 -2
- package/guides/qa-builds.md +16 -12
- package/guides/sdk-reference.json +80 -26
- package/package.json +2 -2
|
@@ -161,7 +161,7 @@ function createPurchaseCoordinator(deps) {
|
|
|
161
161
|
if (!isCurrent(session)) return noDelivery();
|
|
162
162
|
const wasAllowed = item.finishAllowed;
|
|
163
163
|
if (delivered.signInLost && !wasAllowed) {
|
|
164
|
-
blockWithoutSession(item
|
|
164
|
+
blockWithoutSession(item);
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
167
|
if (await deliverItem(session, item, context)) delivered.finished = true;
|
|
@@ -170,9 +170,8 @@ function createPurchaseCoordinator(deps) {
|
|
|
170
170
|
}
|
|
171
171
|
return delivered;
|
|
172
172
|
}
|
|
173
|
-
function blockWithoutSession(item
|
|
174
|
-
|
|
175
|
-
block(item.state, foreign ? "ownershipConflict" : "signInRequired", null);
|
|
173
|
+
function blockWithoutSession(item) {
|
|
174
|
+
block(item.state, item.transaction.appAccountToken === null ? "ownershipConflict" : "signInRequired", null);
|
|
176
175
|
}
|
|
177
176
|
async function deliverItem(session, item, context) {
|
|
178
177
|
if (!item.finishAllowed) {
|
|
@@ -181,7 +180,7 @@ function createPurchaseCoordinator(deps) {
|
|
|
181
180
|
return complete(session, item);
|
|
182
181
|
}
|
|
183
182
|
async function confirmAccess(session, item, context) {
|
|
184
|
-
if (
|
|
183
|
+
if (item.transaction.appAccountToken === null) {
|
|
185
184
|
block(item.state, "ownershipConflict", null);
|
|
186
185
|
return false;
|
|
187
186
|
}
|
|
@@ -373,6 +372,7 @@ function createPurchaseCoordinator(deps) {
|
|
|
373
372
|
const known = new Set([...items.keys()].filter((key) => items.get(key).state.productId === context.productId));
|
|
374
373
|
pendingApproval = { productId: context.productId, token: context.appAccountToken, known };
|
|
375
374
|
setOutcome("awaitingApproval");
|
|
375
|
+
deps.onAwaitingApproval?.(context.productId);
|
|
376
376
|
return true;
|
|
377
377
|
}
|
|
378
378
|
function setOutcome(next) {
|
|
@@ -464,10 +464,10 @@ function decodeContext(settled, isContext) {
|
|
|
464
464
|
return { kind: "unavailable", retryAfterMs: retryAfter(raw.retryAfterMs) };
|
|
465
465
|
}
|
|
466
466
|
function isPurchaseContext(value) {
|
|
467
|
-
return isMemberContext(value) && isObject(value) && isNonEmpty(value.productId);
|
|
467
|
+
return isMemberContext(value) && isObject(value) && isNonEmpty(value.productId) && isNonEmpty(value.appAccountToken);
|
|
468
468
|
}
|
|
469
469
|
function isMemberContext(value) {
|
|
470
|
-
return isObject(value) && isNonEmpty(value.
|
|
470
|
+
return isObject(value) && isNonEmpty(value.scopeKey);
|
|
471
471
|
}
|
|
472
472
|
function retryAfter(value) {
|
|
473
473
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
@@ -529,6 +529,14 @@ function createSerialQueue() {
|
|
|
529
529
|
};
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
+
// ../../core/sdk/iap/src/host-session.ts
|
|
533
|
+
function readIdentityBound() {
|
|
534
|
+
return wixApiGlobal()?.identityBound === true;
|
|
535
|
+
}
|
|
536
|
+
function wixApiGlobal() {
|
|
537
|
+
return globalThis.himalaya?.wixApi;
|
|
538
|
+
}
|
|
539
|
+
|
|
532
540
|
// ../../core/sdk/iap/src/index.ts
|
|
533
541
|
async function purchaseForVerification(input) {
|
|
534
542
|
if (!nonempty(input?.productId) || !isUUID2(input?.appAccountToken)) return failure2("invalidArgument");
|
|
@@ -661,6 +669,7 @@ var COMPLETE_PAYMENT_REFUSALS = {
|
|
|
661
669
|
PURCHASE_REVOKED: "refusedFinal",
|
|
662
670
|
ZERO_PRICE: "refusedFinal",
|
|
663
671
|
NO_MATCHING_PAYMENT: "refusedFinal",
|
|
672
|
+
PAYMENT_NOT_PENDING: "refusedFinal",
|
|
664
673
|
// Before recordPurchase: nothing is recorded. Held pending owner confirmation.
|
|
665
674
|
INVALID_PURCHASE: "hold",
|
|
666
675
|
APP_NOT_CONFIGURED: "hold",
|
|
@@ -702,8 +711,8 @@ function createWorkerOrderRefStore(storage) {
|
|
|
702
711
|
}
|
|
703
712
|
function hasRefs(app) {
|
|
704
713
|
if (!isUUID3(app.purchasesAppId) || !isUUID3(app.siteId)) return null;
|
|
705
|
-
const
|
|
706
|
-
return
|
|
714
|
+
const members = readIndex(kv(), indexKey(app));
|
|
715
|
+
return members && members.length > 0;
|
|
707
716
|
}
|
|
708
717
|
return { open, hasRefs };
|
|
709
718
|
}
|
|
@@ -715,12 +724,12 @@ function upsert(refs, ref) {
|
|
|
715
724
|
}
|
|
716
725
|
function updateIndex(store, key, holdsRefs) {
|
|
717
726
|
const location = indexKey(key);
|
|
718
|
-
const
|
|
719
|
-
if (
|
|
720
|
-
const
|
|
721
|
-
const others =
|
|
722
|
-
const next = holdsRefs ? [...others,
|
|
723
|
-
return next.length ===
|
|
727
|
+
const members = readIndex(store, location);
|
|
728
|
+
if (members === null) return false;
|
|
729
|
+
const member = memberKey(key.memberId);
|
|
730
|
+
const others = members.filter((stored) => stored !== member);
|
|
731
|
+
const next = holdsRefs ? [...others, member] : others;
|
|
732
|
+
return next.length === members.length && next.every((m, i) => m === members[i]) || writeVerified(store, location, serialize({ v: VERSION, members: next }));
|
|
724
733
|
}
|
|
725
734
|
function readRefs(store, location) {
|
|
726
735
|
const parsed = readVersioned(store, location);
|
|
@@ -731,8 +740,8 @@ function readRefs(store, location) {
|
|
|
731
740
|
function readIndex(store, location) {
|
|
732
741
|
const parsed = readVersioned(store, location);
|
|
733
742
|
if (parsed === "empty") return [];
|
|
734
|
-
if (!parsed || !Array.isArray(parsed.
|
|
735
|
-
return parsed.
|
|
743
|
+
if (!parsed || !Array.isArray(parsed.members) || !parsed.members.every(isMemberKey)) return null;
|
|
744
|
+
return parsed.members;
|
|
736
745
|
}
|
|
737
746
|
function readVersioned(store, location) {
|
|
738
747
|
if (!store) return null;
|
|
@@ -752,18 +761,18 @@ function writeVerified(store, location, value) {
|
|
|
752
761
|
function isOrderRef(value) {
|
|
753
762
|
if (!isObject4(value) || typeof value.key !== "string" || !STAGES.has(value.stage)) return false;
|
|
754
763
|
if (typeof value.updatedAt !== "number") return false;
|
|
755
|
-
const optional = ["kind", "cashierOrderId", "wixTransactionId", "paymentId", "storeProductId", "appProductId", "appleTransactionId"];
|
|
764
|
+
const optional = ["kind", "cashierOrderId", "wixTransactionId", "paymentId", "storeProductId", "appProductId", "appleTransactionId", "appAccountToken"];
|
|
756
765
|
if (!optional.every((field) => value[field] === void 0 || typeof value[field] === "string")) return false;
|
|
757
766
|
return value.verticalRef === void 0 || isStringMap(value.verticalRef);
|
|
758
767
|
}
|
|
759
768
|
function isPartitionKey(key) {
|
|
760
|
-
return isUUID3(key.purchasesAppId) && isUUID3(key.siteId) && isUUID3(key.
|
|
769
|
+
return isUUID3(key.purchasesAppId) && isUUID3(key.siteId) && isUUID3(key.memberId);
|
|
761
770
|
}
|
|
762
771
|
function partitionKey(key) {
|
|
763
|
-
return `${PREFIX}.${key.purchasesAppId}.${key.siteId}.${
|
|
772
|
+
return `${PREFIX}.${key.purchasesAppId}.${key.siteId}.${memberKey(key.memberId)}`;
|
|
764
773
|
}
|
|
765
|
-
function
|
|
766
|
-
const text =
|
|
774
|
+
function memberKey(memberId) {
|
|
775
|
+
const text = memberId.toLowerCase();
|
|
767
776
|
let h1 = 3735928559;
|
|
768
777
|
let h2 = 1103547991;
|
|
769
778
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -773,13 +782,13 @@ function customerKey(customerId) {
|
|
|
773
782
|
}
|
|
774
783
|
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
775
784
|
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
776
|
-
return `
|
|
785
|
+
return `m${(4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, "0")}`;
|
|
777
786
|
}
|
|
778
|
-
function
|
|
779
|
-
return typeof value === "string" && /^
|
|
787
|
+
function isMemberKey(value) {
|
|
788
|
+
return typeof value === "string" && /^m[0-9a-f]{14}$/.test(value);
|
|
780
789
|
}
|
|
781
790
|
function indexKey(app) {
|
|
782
|
-
return `${PREFIX}.${app.purchasesAppId}.${app.siteId}.
|
|
791
|
+
return `${PREFIX}.${app.purchasesAppId}.${app.siteId}.members`;
|
|
783
792
|
}
|
|
784
793
|
function hostStorage() {
|
|
785
794
|
const storage = globalThis.himalaya?.storage;
|
|
@@ -815,7 +824,9 @@ function bindBridge(session, provider, bridge) {
|
|
|
815
824
|
const isCurrent = () => session.kind === "member" && provider.current().generation === session.generation;
|
|
816
825
|
async function call(method, path, body) {
|
|
817
826
|
if (!isCurrent()) return SESSION_CHANGED;
|
|
818
|
-
const
|
|
827
|
+
const expectation = boundExpectation(session, provider, bridge);
|
|
828
|
+
if (expectation === "refuse") return { status: "notEnabled" };
|
|
829
|
+
const outcome = await attempt(bridge, method, path, body, expectation);
|
|
819
830
|
return isCurrent() ? outcome : SESSION_CHANGED;
|
|
820
831
|
}
|
|
821
832
|
return { call, isCurrent };
|
|
@@ -826,13 +837,25 @@ function hostBridge() {
|
|
|
826
837
|
return typeof bridge?.call === "function" ? bridge : void 0;
|
|
827
838
|
}
|
|
828
839
|
var MISSING_BRIDGE = { call: () => Promise.reject(new Error("wixApi not enabled")) };
|
|
829
|
-
async function attempt(bridge, method, path, body) {
|
|
840
|
+
async function attempt(bridge, method, path, body, expectation) {
|
|
830
841
|
try {
|
|
831
|
-
|
|
842
|
+
const answer = expectation ? await bridge.call(method, path, body, void 0, expectation) : await bridge.call(method, path, body);
|
|
843
|
+
return { status: "ok", body: answer };
|
|
832
844
|
} catch (error) {
|
|
833
845
|
return refusalOf(error);
|
|
834
846
|
}
|
|
835
847
|
}
|
|
848
|
+
function boundExpectation(session, provider, bridge) {
|
|
849
|
+
const source = provider;
|
|
850
|
+
const live = isLiveHost(bridge);
|
|
851
|
+
if (live && (source.identityBound !== true || !readIdentityBound())) return "refuse";
|
|
852
|
+
if (typeof source.identity !== "function") return live ? "refuse" : void 0;
|
|
853
|
+
return source.identity(session) ?? "refuse";
|
|
854
|
+
}
|
|
855
|
+
function isLiveHost(bridge) {
|
|
856
|
+
const api = globalThis.himalaya?.wixApi;
|
|
857
|
+
return api != null && (bridge === api || bridge.call === api.call);
|
|
858
|
+
}
|
|
836
859
|
function refusalOf(error) {
|
|
837
860
|
const message = error?.message;
|
|
838
861
|
const text = typeof message === "string" ? message : "";
|
|
@@ -863,16 +886,17 @@ function createWixOrderBackend(deps) {
|
|
|
863
886
|
if (!isCurrent(session)) return STALE2;
|
|
864
887
|
if (binding === void 0) return retry("unavailable");
|
|
865
888
|
if (!binding) return stopWith("rejected", "notConfigured");
|
|
866
|
-
const
|
|
889
|
+
const memberId = await readMemberId(deps.identity, session);
|
|
867
890
|
if (!isCurrent(session)) return STALE2;
|
|
868
|
-
if (
|
|
869
|
-
|
|
891
|
+
if (memberId === void 0) return retry("unavailable");
|
|
892
|
+
if (!memberId) return SIGN_IN;
|
|
893
|
+
const member = memberFor(binding, memberId);
|
|
870
894
|
cached = { generation: session.generation, member };
|
|
871
895
|
return member;
|
|
872
896
|
}
|
|
873
|
-
function memberFor(binding,
|
|
874
|
-
const partition = deps.refs.open({ purchasesAppId: binding.purchasesAppId, siteId: binding.siteId,
|
|
875
|
-
return {
|
|
897
|
+
function memberFor(binding, memberId) {
|
|
898
|
+
const partition = deps.refs.open({ purchasesAppId: binding.purchasesAppId, siteId: binding.siteId, memberId });
|
|
899
|
+
return { memberId, scopeKey: `${binding.purchasesAppId}:${memberId.toLowerCase()}`, partition };
|
|
876
900
|
}
|
|
877
901
|
async function getPurchaseContext(session, catalogRef) {
|
|
878
902
|
const sale = await saleFor(session, catalogRef);
|
|
@@ -909,6 +933,7 @@ function createWixOrderBackend(deps) {
|
|
|
909
933
|
if (existing?.stage === "submitted") return stopWith("rejected", "paymentInProgress");
|
|
910
934
|
if (existing?.stage === "held") return stopWith("rejected", "paymentHeld");
|
|
911
935
|
if (existing?.stage === "needsSupport") return stopWith("rejected", "needsSupport");
|
|
936
|
+
if (deps.productBusy?.(sale.appleProduct.productId)) return stopWith("rejected", "paymentInProgress");
|
|
912
937
|
const attempt2 = existing?.cashierOrderId && existing.verticalRef ? await reopenOrOpen(sale, { cashierOrderId: existing.cashierOrderId, verticalRef: existing.verticalRef }) : await sale.adapter.openOrder(sale.item, sale.bridge);
|
|
913
938
|
return recordOrder(sale, existing, attempt2);
|
|
914
939
|
}
|
|
@@ -977,14 +1002,14 @@ function createWixOrderBackend(deps) {
|
|
|
977
1002
|
stage: "started",
|
|
978
1003
|
paymentId: started.payment.paymentId,
|
|
979
1004
|
storeProductId: started.storeProductId,
|
|
1005
|
+
appAccountToken: started.appAccountToken.toLowerCase(),
|
|
980
1006
|
...started.payment.appProductId ? { appProductId: started.payment.appProductId } : {},
|
|
981
1007
|
updatedAt: now()
|
|
982
1008
|
});
|
|
983
1009
|
if (isStop(recorded)) return contextResult(sale.catalogRef, recorded);
|
|
984
|
-
return { status: "ok", context: { productId: started.storeProductId, appAccountToken:
|
|
1010
|
+
return { status: "ok", context: { productId: started.storeProductId, appAccountToken: started.appAccountToken, scopeKey: sale.member.scopeKey } };
|
|
985
1011
|
}
|
|
986
1012
|
function startedMismatch(sale, started) {
|
|
987
|
-
if (!sameUUID(started.customerId, sale.member.customerId)) return "invalidAssociation";
|
|
988
1013
|
return sale.appleProduct.productId === started.storeProductId ? null : "productChanged";
|
|
989
1014
|
}
|
|
990
1015
|
async function startRefusal(sale, ref, failure3) {
|
|
@@ -1019,9 +1044,8 @@ function createWixOrderBackend(deps) {
|
|
|
1019
1044
|
}
|
|
1020
1045
|
async function matchedProof(proof, payment) {
|
|
1021
1046
|
const refs = proof.member.partition?.refs() ?? [];
|
|
1022
|
-
const ref = refs.find((stored) => stored.paymentId === payment.paymentId) ?? refs.find((stored) => stored.wixTransactionId === payment.wixTransactionId);
|
|
1047
|
+
const ref = refs.find((stored) => stored.paymentId === payment.paymentId) ?? refs.find((stored) => stored.wixTransactionId === payment.wixTransactionId) ?? refByToken(refs, proof.transaction);
|
|
1023
1048
|
if (ref?.storeProductId && ref.storeProductId !== proof.transaction.productId) return holdAssociation(ref);
|
|
1024
|
-
reportOtherOrders(refs, ref, payment, proof.transaction);
|
|
1025
1049
|
const tracking = trackingRef(ref, payment, proof.transaction, now());
|
|
1026
1050
|
if (isFinalRefusal(payment.status)) return finalRefusal(proof, tracking);
|
|
1027
1051
|
if (payment.status === "pending") return RETRYABLE;
|
|
@@ -1036,12 +1060,6 @@ function createWixOrderBackend(deps) {
|
|
|
1036
1060
|
deps.report({ key: ref.key, failure: "invalidAssociation" });
|
|
1037
1061
|
return { status: "rejected" };
|
|
1038
1062
|
}
|
|
1039
|
-
function reportOtherOrders(refs, matched, payment, transaction) {
|
|
1040
|
-
for (const other of refs) {
|
|
1041
|
-
if (other.key === matched?.key || other.stage !== "started" || other.storeProductId !== transaction.productId) continue;
|
|
1042
|
-
if (other.paymentId !== payment.paymentId) deps.report({ key: other.key, failure: "matchedOtherOrder" });
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
1063
|
async function accessGranted(proof, ref) {
|
|
1046
1064
|
const adapter = adapterFor(ref.kind);
|
|
1047
1065
|
if (!adapter || !ref.verticalRef) return false;
|
|
@@ -1064,7 +1082,8 @@ function createWixOrderBackend(deps) {
|
|
|
1064
1082
|
function finalRefusal(proof, ref) {
|
|
1065
1083
|
const hold = ref ?? unassociatedHold(proof.transaction, now());
|
|
1066
1084
|
const partition = proof.member.partition;
|
|
1067
|
-
|
|
1085
|
+
const stored = { ...hold, stage: "needsSupport", appleTransactionId: proof.transaction.transactionId, updatedAt: now() };
|
|
1086
|
+
if (!partition || !isCurrent(proof.session) || !partition.put(stored)) return RETRYABLE;
|
|
1068
1087
|
deps.report({ key: hold.key, access: "needsSupport" });
|
|
1069
1088
|
return { status: "refusedFinal", acknowledgement: proof.acknowledgement };
|
|
1070
1089
|
}
|
|
@@ -1083,12 +1102,16 @@ function createWixOrderBackend(deps) {
|
|
|
1083
1102
|
}
|
|
1084
1103
|
function startedRefFor(proof) {
|
|
1085
1104
|
const refs = proof.member.partition?.refs() ?? [];
|
|
1086
|
-
|
|
1105
|
+
const { productId, transactionId } = proof.transaction;
|
|
1106
|
+
const holding = refs.find((ref) => (ref.stage === "held" || ref.stage === "needsSupport") && ref.appleTransactionId === transactionId);
|
|
1107
|
+
if (holding) return holding;
|
|
1108
|
+
if (proof.transaction.appAccountToken) return refByToken(refs, proof.transaction);
|
|
1109
|
+
return refs.filter((ref) => ref.stage === "started" && ref.storeProductId === productId).reduce((newest, ref) => !newest || ref.updatedAt >= newest.updatedAt ? ref : newest, void 0);
|
|
1087
1110
|
}
|
|
1088
1111
|
async function getMemberContext(session) {
|
|
1089
1112
|
const member = await memberOf(session);
|
|
1090
1113
|
if (isStop(member)) return { status: member.result };
|
|
1091
|
-
return { status: "ok", context: {
|
|
1114
|
+
return { status: "ok", context: { scopeKey: member.scopeKey } };
|
|
1092
1115
|
}
|
|
1093
1116
|
return { getPurchaseContext, getMemberContext, submitProof };
|
|
1094
1117
|
}
|
|
@@ -1142,6 +1165,17 @@ function acknowledgementOf(transaction, scopeKey) {
|
|
|
1142
1165
|
function refAt(partition, key) {
|
|
1143
1166
|
return partition.refs().find((ref) => ref.key === key);
|
|
1144
1167
|
}
|
|
1168
|
+
async function readMemberId(identity, session) {
|
|
1169
|
+
try {
|
|
1170
|
+
return await identity.memberId(session);
|
|
1171
|
+
} catch {
|
|
1172
|
+
return void 0;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function refByToken(refs, transaction) {
|
|
1176
|
+
const token = transaction.appAccountToken?.toLowerCase();
|
|
1177
|
+
return token ? refs.find((ref) => ref.appAccountToken === token) : void 0;
|
|
1178
|
+
}
|
|
1145
1179
|
async function readBinding(source) {
|
|
1146
1180
|
try {
|
|
1147
1181
|
return await source.current();
|
|
@@ -1149,14 +1183,12 @@ async function readBinding(source) {
|
|
|
1149
1183
|
return void 0;
|
|
1150
1184
|
}
|
|
1151
1185
|
}
|
|
1152
|
-
function sameUUID(a, b) {
|
|
1153
|
-
return a.toLowerCase() === b.toLowerCase();
|
|
1154
|
-
}
|
|
1155
1186
|
|
|
1156
1187
|
// ../../stdlib/flows/iap-checkout/src/apple-iap-core.ts
|
|
1157
1188
|
function createAppleIapCheckoutWith(deps) {
|
|
1158
1189
|
return deps.enabled ? enabledCheckout(deps) : disabledCheckout();
|
|
1159
1190
|
}
|
|
1191
|
+
var ASK_TO_BUY_HOLD_MS = 60 * 6e4;
|
|
1160
1192
|
function enabledCheckout(deps) {
|
|
1161
1193
|
const refs = deps.refs ?? createWorkerOrderRefStore();
|
|
1162
1194
|
const bridge = deps.bridge ?? hostBridge() ?? MISSING_BRIDGE;
|
|
@@ -1166,10 +1198,11 @@ function enabledCheckout(deps) {
|
|
|
1166
1198
|
let generation = deps.session.current().generation;
|
|
1167
1199
|
let disposed = false;
|
|
1168
1200
|
let tracking = Promise.resolve();
|
|
1169
|
-
let trackedCustomer = null;
|
|
1170
1201
|
let partitionRead = null;
|
|
1202
|
+
const awaitingApproval = /* @__PURE__ */ new Map();
|
|
1171
1203
|
const backend = createWixOrderBackend({
|
|
1172
1204
|
wixIap: deps.wixIap,
|
|
1205
|
+
identity: deps.identity,
|
|
1173
1206
|
binding: deps.binding,
|
|
1174
1207
|
session: deps.session,
|
|
1175
1208
|
clock: deps.clock,
|
|
@@ -1178,6 +1211,7 @@ function enabledCheckout(deps) {
|
|
|
1178
1211
|
refs,
|
|
1179
1212
|
bridge,
|
|
1180
1213
|
itemFor: (key) => sales.get(key),
|
|
1214
|
+
productBusy,
|
|
1181
1215
|
report: onEvent
|
|
1182
1216
|
});
|
|
1183
1217
|
const coordinator = createPurchaseCoordinator({
|
|
@@ -1186,7 +1220,8 @@ function enabledCheckout(deps) {
|
|
|
1186
1220
|
session: deps.session,
|
|
1187
1221
|
clock: deps.clock,
|
|
1188
1222
|
jitter: deps.jitter,
|
|
1189
|
-
onState: publish
|
|
1223
|
+
onState: publish,
|
|
1224
|
+
onAwaitingApproval: reserveAskToBuy
|
|
1190
1225
|
});
|
|
1191
1226
|
async function start() {
|
|
1192
1227
|
resetIfSessionChanged();
|
|
@@ -1264,16 +1299,31 @@ function enabledCheckout(deps) {
|
|
|
1264
1299
|
if (current.failure) return "refused";
|
|
1265
1300
|
if (OVERLAY_PHASES.has(purchase.phase)) return purchase.phase;
|
|
1266
1301
|
if (current.buying) return "preparing";
|
|
1267
|
-
if (mine?.access === "processing" || purchase.items.some(
|
|
1302
|
+
if (mine?.access === "processing" || purchase.items.some(isInFlight)) return "processing";
|
|
1303
|
+
if (!current.item && progress.some((payment) => payment.access === "processing")) return "processing";
|
|
1268
1304
|
return "idle";
|
|
1269
1305
|
}
|
|
1306
|
+
function reserveAskToBuy(productId) {
|
|
1307
|
+
awaitingApproval.set(productId, { items: itemsFor(productId).length, since: deps.clock.now() });
|
|
1308
|
+
}
|
|
1309
|
+
function productBusy(productId) {
|
|
1310
|
+
const items = itemsFor(productId);
|
|
1311
|
+
const pending = awaitingApproval.get(productId);
|
|
1312
|
+
if (pending && (items.length > pending.items || deps.clock.now() - pending.since >= ASK_TO_BUY_HOLD_MS)) {
|
|
1313
|
+
awaitingApproval.delete(productId);
|
|
1314
|
+
}
|
|
1315
|
+
return awaitingApproval.has(productId) || items.some(isUnfinished);
|
|
1316
|
+
}
|
|
1317
|
+
function itemsFor(productId) {
|
|
1318
|
+
return coordinator.getState().items.filter((held) => held.productId === productId);
|
|
1319
|
+
}
|
|
1270
1320
|
function resetIfSessionChanged() {
|
|
1271
1321
|
const next = deps.session.current().generation;
|
|
1272
1322
|
if (next === generation) return;
|
|
1273
1323
|
generation = next;
|
|
1274
1324
|
sales.clear();
|
|
1275
1325
|
payments.clear();
|
|
1276
|
-
|
|
1326
|
+
awaitingApproval.clear();
|
|
1277
1327
|
partitionRead = null;
|
|
1278
1328
|
current = { item: null, failure: null, buying: false };
|
|
1279
1329
|
}
|
|
@@ -1314,17 +1364,17 @@ function enabledCheckout(deps) {
|
|
|
1314
1364
|
if (!binding || !isCurrent(session)) return binding === null ? "none" : null;
|
|
1315
1365
|
const stored = refs.hasRefs(binding);
|
|
1316
1366
|
if (stored !== true) return stored === false ? "none" : null;
|
|
1317
|
-
const
|
|
1318
|
-
if (!
|
|
1319
|
-
const partition = refs.open({ purchasesAppId: binding.purchasesAppId, siteId: binding.siteId,
|
|
1367
|
+
const memberId = await memberIdOf(session);
|
|
1368
|
+
if (!memberId || !isCurrent(session)) return null;
|
|
1369
|
+
const partition = refs.open({ purchasesAppId: binding.purchasesAppId, siteId: binding.siteId, memberId });
|
|
1320
1370
|
return partition && { partition };
|
|
1321
1371
|
}
|
|
1322
|
-
async function
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1372
|
+
async function memberIdOf(session) {
|
|
1373
|
+
try {
|
|
1374
|
+
return await deps.identity.memberId(session);
|
|
1375
|
+
} catch {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1328
1378
|
}
|
|
1329
1379
|
function backOffDue() {
|
|
1330
1380
|
const attempts = (partitionRead?.attempts ?? 0) + 1;
|
|
@@ -1415,7 +1465,11 @@ function blankState(enabled) {
|
|
|
1415
1465
|
purchase: { phase: "idle", errorCode: null, items: [], unfinishedCount: null, nextWakeAt: null }
|
|
1416
1466
|
};
|
|
1417
1467
|
}
|
|
1418
|
-
function
|
|
1468
|
+
function isUnfinished(item) {
|
|
1469
|
+
return item.completion === "pending" || item.completion === "retryRequired";
|
|
1470
|
+
}
|
|
1471
|
+
function isInFlight(item) {
|
|
1472
|
+
if (item.completion === "finished") return false;
|
|
1419
1473
|
return item.access === "processing" || item.access === "confirming" || item.access === "pending" || item.access === "waitingToRetry";
|
|
1420
1474
|
}
|
|
1421
1475
|
function itemKey(item) {
|