purchase method

Starts a purchase after configuration.

Cancellation is a domain result and must not be reported as a transport exception. Implementations must not both navigate and return a redirect URL unless their documented integration contract explicitly requires it.

Implementation

@override
Future<InappifySdkResult<PurchaseResult>> purchase(
  InappifyPurchaseRequest request,
) async {
  final cancelled = _preCancelled<PurchaseResult>(
    'purchase',
    request.cancellation,
  );
  if (cancelled != null) return cancelled;
  final packageIdentifier = _resolvePackageIdentifier(request);
  if (packageIdentifier == null) {
    return _invalid<PurchaseResult>(
      'purchase',
      'A packageIdentifier could not be inferred unambiguously.',
    );
  }
  if (_hasUnsupportedNativePurchaseOptions(request)) {
    return _invalid<PurchaseResult>(
      'purchase',
      'Native-store purchase options are not supported by Web checkout.',
    );
  }
  return _run<PurchaseResult>(
    'purchase',
    action: () async {
      final raw = await _bridge.call(
        'purchase',
        positionalArguments: <Object?>[
          _checkoutParameters(request, packageIdentifier),
          const <String, Object?>{'redirect': false},
        ],
        cancellation: request.cancellation,
        injectAbortSignal: request.cancellation != null,
      );
      final checkout = InappifyWebPayloadCodec.checkout(raw);
      final normalizedStatus = checkout.checkout.status.toLowerCase();
      if (normalizedStatus == 'cancelled' || normalizedStatus == 'canceled') {
        _bridge.clearPendingCheckout();
        throw _WebSemanticException(
          code: InappifyFailureCode.purchaseCancelled,
          status: checkout.checkout.status,
        );
      }
      if (normalizedStatus == 'failed' || normalizedStatus == 'expired') {
        _bridge.clearPendingCheckout();
        throw _WebSemanticException(
          code: InappifyFailureCode.unknown,
          status: checkout.checkout.status,
        );
      }
      if (checkout.isPaid) {
        _bridge.clearPendingCheckout();
        _readCustomerPropertyBestEffort();
      } else {
        _bridge.persistPendingCheckout(checkout.checkout.id);
      }
      return PurchaseResult(
        url: checkout.nextAction.url,
        purchaseStatus: checkout.isPaid
            ? PurchaseStatus.DONE
            : PurchaseStatus.NEEDTOPAY,
        checkoutId: checkout.checkout.id,
        checkoutStatus: checkout.checkout.status,
        nextActionType: checkout.nextAction.type,
      );
    },
    cancellation: request.cancellation,
    mutationMayHaveCommitted: true,
  );
}