@tinkerlandai/indiana-react-native 0.9.5 → 0.10.1

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.
@@ -61,6 +61,7 @@ enum IndianaCustomerResponseSemantics {
61
61
  }
62
62
  static func object(_ input: O, fields: Set<String>, path: [String]) throws -> O {
63
63
  var v = input
64
+ try IndianaMerchantCommerceSemantics.validate(v, fields: fields)
64
65
  func has(_ keys: [String]) -> Bool { Set(keys).isSubset(of: fields) }
65
66
  if has(["productKind", "seatBundle", "applePromotionalOffer", "replacement", "offerKind"]) {
66
67
  let apple = s(v["provider"]) == "apple", member = s(v["productKind"]) == "membership", kind = s(v["offerKind"]), replacement = o(v["replacement"]), proof = o(v["applePromotionalOffer"])
@@ -107,7 +108,7 @@ enum IndianaCustomerResponseSemantics {
107
108
  if has(["operationId", "lines", "discount", "charge", "nativeOffer", "execution"]) { try attachQuote(v) }
108
109
  if has(["executor", "checkoutUrl", "execution"]) {
109
110
  try require(nilValue(v["execution"]) || s(v["executor"]) == "merchant")
110
- if has(["quoteFinalized", "orderId"]) { try require(nilValue(v["checkoutUrl"]) || s(v["executor"]) == "indiana") }
111
+ if has(["quoteFinalized", "orderId"]) { try require(nilValue(v["checkoutUrl"]) || s(v["executor"]) == "indiana" || s(o(v["merchantExecution"])["type"]) == "purchase") }
111
112
  if !nilValue(v["checkoutUrl"]) { try require(URL(string: s(v["checkoutUrl"]))?.scheme == "https") }
112
113
  }
113
114
  if has(["action", "executor", "update", "cancel", "schedule", "release"]) {
@@ -187,7 +188,10 @@ enum IndianaCustomerResponseSemantics {
187
188
  }
188
189
  static func attachQuote(_ v: O) throws {
189
190
  let lines = a(v["lines"]), trial = o(lines.first?["trial"]), execution = o(v["execution"]), discount = o(v["discount"]), provider = s(v["provider"])
190
- if provider == "stripe" {
191
+ if provider == "merchant" {
192
+ try require(s(execution["provider"]) == "merchant" && s(execution["operationId"]) == s(v["operationId"]) && n(execution["catalogAmountMinor"]) == n(v["catalogAmountMinor"]) && s(execution["currency"]) == s(v["currency"]) && time(execution["expiresAt"]) <= time(v["expiresAt"]))
193
+ try require(nilValue(execution["trialPeriodDays"]) == trial.isEmpty && (trial.isEmpty || n(execution["trialPeriodDays"]) == n(trial["days"])) && s(o(execution["discount"])["grantId"]) == s(discount["offerId"]) && v["nativeOffer"] == nil)
194
+ } else if provider == "stripe" {
191
195
  if trial["cardRequired"] as? Bool == false && o(execution["invoiceMode"])["enabled"] as? Bool != true { try require(execution.isEmpty && discount.isEmpty) }
192
196
  else {
193
197
  try require(!execution.isEmpty && s(o(execution["metadata"])["grooveCheckoutId"]) == s(v["operationId"]) && time(execution["expiresAt"]) <= time(v["expiresAt"]))
@@ -200,7 +204,7 @@ enum IndianaCustomerResponseSemantics {
200
204
  try require(v["nativeOffer"] == nil)
201
205
  } else { let selection = o(v["nativeOffer"]); try require(execution.isEmpty && !selection.isEmpty && lines.count == 1 && n(selection["discountMinor"]) <= n(v["catalogAmountMinor"]) && nilValue(selection["trialDays"]) == trial.isEmpty && (trial.isEmpty || n(selection["trialDays"]) == n(trial["days"])) && (discount.isEmpty || n(selection["discountMinor"]) == n(discount["amountMinor"]))) }
202
206
  if !discount.isEmpty {
203
- let allocations = a(discount["allocations"]); try require(nilValue(v["billingSubscriptionId"]) && (provider == "stripe" || s(o(discount["duration"])["type"]) == "once") && n(discount["amountMinor"]) <= n(v["catalogAmountMinor"]) && allocations.count == lines.count)
207
+ let allocations = a(discount["allocations"]); try require(nilValue(v["billingSubscriptionId"]) && (["stripe", "merchant"].contains(provider) || s(o(discount["duration"])["type"]) == "once") && n(discount["amountMinor"]) <= n(v["catalogAmountMinor"]) && allocations.count == lines.count)
204
208
  for (row, line) in zip(allocations, lines) { try require(s(row["productVersionId"]) == s(line["productVersionId"]) && n(row["amountMinor"]) <= n(line["catalogAmountMinor"])) }; try require(allocations.reduce(0) { $0 + n($1["amountMinor"]) } == n(discount["amountMinor"]))
205
209
  }
206
210
  if let charge = v["charge"] as? O { try require(!nilValue(v["billingSubscriptionId"]) && n(charge["totalMinor"]) == n(charge["debitMinor"]) - n(charge["discountMinor"]) + n(charge["taxMinor"]) && n(charge["discountMinor"]) <= n(charge["debitMinor"]) && time(charge["periodEndsAt"]) > time(charge["effectiveAt"])) }
@@ -31,9 +31,9 @@ enum IndianaCustomerSemantics {
31
31
  let plans = array(value["plans"]), provider = value["provider"]?.string
32
32
  try require(Set(plans.compactMap { $0.object?["productVersionId"]?.string }).count == plans.count)
33
33
  try require(plans.allSatisfy { ($0.object?["featureQuantities"]?.object?.count ?? 0) <= 100 })
34
- try require(value["offerId"] == nil || value["billingSubscriptionId"] == nil && (provider == "stripe" || value["nativeOffer"] != nil))
35
- try require(value["nativeOffer"] == nil || provider != "stripe" && plans.count == 1)
36
- try require(value["newBillingSubscription"] == nil || provider == "stripe" && value["billingSubscriptionId"] == nil)
34
+ try require(value["offerId"] == nil || value["billingSubscriptionId"] == nil && (["stripe", "merchant"].contains(provider) || value["nativeOffer"] != nil))
35
+ try require(value["nativeOffer"] == nil || ["apple", "google"].contains(provider) && plans.count == 1)
36
+ try require(value["newBillingSubscription"] == nil || ["stripe", "merchant"].contains(provider) && value["billingSubscriptionId"] == nil)
37
37
  try require(value["invoiceMode"]?.object?["enabled"] != .bool(true) || provider == "stripe" && value["entityId"] == nil)
38
38
  if let offer = value["nativeOffer"]?.object {
39
39
  try require(offer["offerKind"] != .string("none") || offer["offerId"] == .null && offer["discountMinor"] == .number(0) && offer["trialDays"] == .null)
@@ -2,7 +2,7 @@
2
2
  import Foundation
3
3
  public enum IndianaEnvironment: String, Codable, Sendable { case sandbox, live }
4
4
  enum IndianaProtocol {
5
- static let wireProtocol = "0.9"
5
+ static let wireProtocol = "0.10"
6
6
  static let uuidPattern = "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
7
7
  static let utcPattern = "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
8
8
  static let scopeKeys: Set<String> = ["projectId", "environment"]
@@ -1,6 +1,6 @@
1
1
  import Foundation
2
2
 
3
- public enum IndianaSubscriptionProvider: String, Sendable { case stripe, apple, google }
3
+ public enum IndianaSubscriptionProvider: String, Sendable { case stripe, apple, google, merchant }
4
4
  public struct IndianaSubscriptionPlan: Sendable {
5
5
  public let productVersionId: UUID
6
6
  public let seatQuantities: [String: Int]
@@ -502,7 +502,7 @@ public actor IndianaClient {
502
502
  var fields: [String: IndianaJSON] = ["requestId":.string(requestId.uuidString.lowercased()), "plans":.array(entries), "provider":.string(provider.rawValue), "currency":.string(currency)]
503
503
  if let billingSubscriptionId { fields["billingSubscriptionId"] = .string(billingSubscriptionId.uuidString.lowercased()) }
504
504
  guard nativeOffer == nil || (nativeOffer!.matches(provider) && plans.count == 1),
505
- offerId == nil || (billingSubscriptionId == nil && (provider == .stripe || nativeOffer != nil)) else { throw IndianaClientError.invalidResponse }
505
+ offerId == nil || (billingSubscriptionId == nil && (provider == .stripe || provider == .merchant || nativeOffer != nil)) else { throw IndianaClientError.invalidResponse }
506
506
  if let offerId { fields["offerId"] = .string(offerId.uuidString.lowercased()) }
507
507
  if let nativeOffer { fields["nativeOffer"] = nativeOffer.wire }
508
508
  let body: IndianaJSON = .object(fields)
@@ -0,0 +1,51 @@
1
+ import Foundation
2
+
3
+ /// Merchant refinements dispatch on generated DTO fields, not user JSON.
4
+ enum IndianaMerchantCommerceSemantics {
5
+ static func validate(_ v: [String: Any], fields: Set<String>) throws {
6
+ func has(_ keys: [String]) -> Bool { Set(keys).isSubset(of: fields) }
7
+ let require = IndianaCustomerResponseSemantics.require
8
+ let o = IndianaCustomerResponseSemantics.o, a = IndianaCustomerResponseSemantics.a
9
+ let s = IndianaCustomerResponseSemantics.s, n = IndianaCustomerResponseSemantics.n
10
+ let nilValue = IndianaCustomerResponseSemantics.nilValue
11
+ if has(["configurationRevision", "connectionRevision", "lineItems", "catalogAmountMinor", "discount"]) {
12
+ let lines = a(v["lineItems"]), discount = o(v["discount"])
13
+ try require(lines.reduce(0) { $0 + n($1["totalMinor"]) } == n(v["catalogAmountMinor"]) && (discount.isEmpty || n(discount["amountMinor"]) <= n(v["catalogAmountMinor"])) && Set(lines.map { s($0["priceId"]) }).count == lines.count)
14
+ }
15
+ if has(["phase", "code", "reason", "retryable", "recovery"]), s(v["reason"]) == "RESULT_UNKNOWN" {
16
+ try require(v["retryable"] as? Bool == false && s(v["recovery"]) == "recover_operation")
17
+ }
18
+ if has(["merchantExecutionId", "paymentStatus", "checkoutUrl", "checkoutExpiresAt"]), !nilValue(v["checkoutUrl"]) {
19
+ try require(["not_started", "pending"].contains(s(v["paymentStatus"])) && !nilValue(v["checkoutExpiresAt"]))
20
+ }
21
+ if has(["merchantExecutionId", "scheduledFor", "status"]) || has(["provider", "eventId", "subscription", "scheduledFor", "status"]) {
22
+ try require((s(v["status"]) == "scheduled") == !nilValue(v["scheduledFor"]))
23
+ }
24
+ if has(["merchantExecutionId", "merchantRefundId", "status"]), s(v["status"]) == "succeeded" { try require(!nilValue(v["merchantRefundId"])) }
25
+ if has(["lookup", "operationId", "customerId", "execution"]), let execution = v["execution"] as? [String: Any] {
26
+ try require(s(execution["operationId"]) == s(v["operationId"]) && s(execution["customerId"]) == s(v["customerId"]))
27
+ }
28
+ if has(["scope", "operationId", "customerId", "confirmedAt", "initialExecutionAllowed", "instruction"]) {
29
+ let instruction = o(v["instruction"])
30
+ try require(s(instruction["operationId"]) == s(v["operationId"]) && s(instruction["customerId"]) == s(v["customerId"]))
31
+ }
32
+ if has(["merchantSubscriptionId", "periodStartsAt", "periodEndsAt", "items"]) {
33
+ let items = a(v["items"])
34
+ try require(IndianaCustomerResponseSemantics.time(v["periodEndsAt"]) > IndianaCustomerResponseSemantics.time(v["periodStartsAt"]) && Set(items.map { s($0["priceId"]) }).count == items.count)
35
+ }
36
+ if has(["merchantInvoiceId", "subtotalMinor", "discountMinor", "taxMinor", "totalMinor", "paidMinor"]) {
37
+ try require(n(v["subtotalMinor"]) - n(v["discountMinor"]) + n(v["taxMinor"]) == n(v["totalMinor"]) && n(v["discountMinor"]) <= n(v["subtotalMinor"]) && n(v["paidMinor"]) == n(v["totalMinor"]))
38
+ }
39
+ if has(["merchantInvoiceId", "lines", "paidMinor"]) {
40
+ let lines = a(v["lines"])
41
+ try require(Set(lines.map { s($0["merchantLineId"]) }).count == lines.count)
42
+ for key in ["subtotalMinor", "discountMinor", "taxMinor", "totalMinor"] { try require(lines.reduce(0) { $0 + n($1[key]) } == n(v[key])) }
43
+ }
44
+ if has(["merchantLineId", "subtotalMinor", "discountMinor", "taxMinor", "totalMinor"]) {
45
+ let subtotal = n(v["subtotalMinor"]), discount = n(v["discountMinor"]), tax = n(v["taxMinor"])
46
+ try require(subtotal - discount + tax == n(v["totalMinor"]) && (subtotal < 0 ? discount == 0 && tax == 0 : discount <= subtotal))
47
+ }
48
+ if has(["provider", "eventId", "merchantOrderId", "paymentStatus", "invoice"]), s(v["paymentStatus"]) == "paid" { try require(!nilValue(v["invoice"])) }
49
+ if has(["provider", "merchantInvoiceId", "cancelSubscriptions", "entitlementAction", "amountMinor"]) || has(["provider", "eventId", "merchantRefundId", "amountMinor", "status"]) { try require(n(v["amountMinor"]) > 0) }
50
+ }
51
+ }
@@ -24,7 +24,7 @@ public struct IndianaNativeStoreOffer: Sendable {
24
24
 
25
25
  func matches(_ provider: IndianaSubscriptionProvider) -> Bool {
26
26
  switch provider {
27
- case .stripe: return false
27
+ case .stripe, .merchant: return false
28
28
  case .apple: return (offerKind == .promotional) == (offerID != nil)
29
29
  case .google:
30
30
  return (offerKind == .none) == (offerID == nil) &&
@@ -1,4 +1,4 @@
1
1
  // Generated by scripts/sdk/generate-native-protocol.mjs. Do not edit.
2
2
  public enum IndianaSDK {
3
- public static let version = "0.9.5"
3
+ public static let version = "0.10.1"
4
4
  }