[CP-7427] fix: Don't check amounts and don't credit for auto-renewing subscriptions

Refs: https://gitlab.protontech.ch/apple/shared/protoncore/-/merge_requests/1657
This commit is contained in:
Victor Jalencas 2024-02-08 11:20:18 +00:00
commit f3b810e6f7
4 changed files with 34 additions and 23 deletions

View file

@ -133,7 +133,7 @@ protocol PaymentsApiProtocol {
/// Get the status of any vendor
func paymentStatusRequest(api: APIService) -> PaymentStatusRequest
func buySubscriptionRequest(
api: APIService, planId: String, amount: Int, amountDue: Int, cycle: Int, paymentAction: PaymentAction
api: APIService, planId: String, amount: Int, amountDue: Int, cycle: Int, paymentAction: PaymentAction, isCreditingAllowed: Bool
) throws -> SubscriptionRequest
func buySubscriptionForZeroRequest(api: APIService, planId: String) -> SubscriptionRequest
/// Get current subscription
@ -158,21 +158,30 @@ class PaymentsApiImplementation: PaymentsApiProtocol {
PaymentStatusRequest(api: api)
}
func buySubscriptionRequest(api: APIService, planId: String, amount: Int, amountDue: Int, cycle: Int, paymentAction: PaymentAction) throws -> SubscriptionRequest {
guard Thread.isMainThread == false else {
assertionFailure("This is a blocking network request, should never be called from main thread")
throw AwaitInternalError.synchronousCallPerformedFromTheMainThread
}
if amountDue == amount {
// if amountDue is equal to amount, request subscription
return SubscriptionRequest(api: api, planId: planId, amount: amount, cycle: cycle, paymentAction: paymentAction)
} else {
// if amountDue is not equal to amount, request credit for a full amount
let creditReq = creditRequest(api: api, amount: amount, paymentAction: paymentAction)
_ = try creditReq.awaitResponse(responseObject: CreditResponse())
// then request subscription for amountDue = 0
return SubscriptionRequest(api: api, planId: planId, amount: 0, cycle: cycle, paymentAction: paymentAction)
}
func buySubscriptionRequest(api: APIService,
planId: String,
amount: Int,
amountDue: Int,
cycle: Int,
paymentAction: PaymentAction,
isCreditingAllowed: Bool) throws -> SubscriptionRequest {
guard Thread.isMainThread == false else {
assertionFailure("This is a blocking network request, should never be called from main thread")
throw AwaitInternalError.synchronousCallPerformedFromTheMainThread
}
guard isCreditingAllowed else { // dynamic plans case
return SubscriptionRequest(api: api, planId: planId, amount: amount, cycle: cycle, paymentAction: paymentAction)
}
if amountDue == amount {
// if amountDue is equal to amount, request subscription
return SubscriptionRequest(api: api, planId: planId, amount: amount, cycle: cycle, paymentAction: paymentAction)
} else {
// if amountDue is not equal to amount, request credit for a full amount
let creditReq = creditRequest(api: api, amount: amount, paymentAction: paymentAction)
_ = try creditReq.awaitResponse(responseObject: CreditResponse())
// then request subscription for amountDue = 0
return SubscriptionRequest(api: api, planId: planId, amount: 0, cycle: cycle, paymentAction: paymentAction)
}
}
func buySubscriptionForZeroRequest(api: APIService, planId: String) -> SubscriptionRequest {

View file

@ -44,12 +44,12 @@ final class ProcessAuthenticated: ProcessProtocol {
unowned let dependencies: ProcessDependencies
let tokenHandler: TokenHandler?
private let featureFlagsRepository: FeatureFlagsRepositoryProtocol
let areSubscriptionsEnabled: Bool
init(dependencies: ProcessDependencies, featureFlagsRepository: FeatureFlagsRepositoryProtocol = FeatureFlagsRepository.shared) {
self.dependencies = dependencies
self.tokenHandler = TokenHandler(dependencies: dependencies)
self.featureFlagsRepository = featureFlagsRepository
self.areSubscriptionsEnabled = featureFlagsRepository.isEnabled(CoreFeatureFlagType.dynamicPlan)
}
let queue = DispatchQueue(label: "ProcessAuthenticated async queue", qos: .userInitiated)
@ -111,7 +111,8 @@ final class ProcessAuthenticated: ProcessProtocol {
amount: plan.amount,
amountDue: plan.amountDue,
cycle: plan.cycle,
paymentAction: .token(token: token.token)
paymentAction: .token(token: token.token),
isCreditingAllowed: !areSubscriptionsEnabled
)
let receiptRes = try request.awaitResponse(responseObject: SubscriptionResponse())
PMLog.debug("StoreKit: success (1)")
@ -135,7 +136,7 @@ final class ProcessAuthenticated: ProcessProtocol {
} catch let error where error.isPaymentAmountMismatchOrUnavailablePlanError {
PMLog.debug("StoreKit: amount mismatch")
if featureFlagsRepository.isEnabled(CoreFeatureFlagType.dynamicPlan){
if areSubscriptionsEnabled {
// we no longer credit the account for this kind of mismatch.
finish(transaction: transaction, result: .errored(.noNewSubscriptionInSuccessfulResponse), completion: completion)
} else {

View file

@ -351,7 +351,8 @@ final class ProcessUnauthenticated: ProcessUnathenticatedProtocol {
amount: plan.amount,
amountDue: plan.amountDue,
cycle: plan.cycle,
paymentAction: .token(token: token.token)
paymentAction: .token(token: token.token),
isCreditingAllowed: !areSubscriptionsEnabled
)
let receiptRes = try request.awaitResponse(responseObject: SubscriptionResponse())
PMLog.debug("StoreKit: success (2)")

View file

@ -38,8 +38,8 @@ public final class PaymentsApiMock: PaymentsApiProtocol {
@ThrowingFuncStub(PaymentsApiProtocol.buySubscriptionRequest, initialReturn: { SubscriptionRequest(api: $0.0, planId: $0.1) }) public var buySubscriptionRequestStub
public func buySubscriptionRequest(
api: APIService, planId: String, amount: Int, amountDue: Int, cycle: Int, paymentAction: PaymentAction
) throws -> SubscriptionRequest { try buySubscriptionRequestStub(api, planId, amount, amountDue, cycle, paymentAction) }
api: APIService, planId: String, amount: Int, amountDue: Int, cycle: Int, paymentAction: PaymentAction, isCreditingAllowed: Bool
) throws -> SubscriptionRequest { try buySubscriptionRequestStub(api, planId, amount, amountDue, cycle, paymentAction, isCreditingAllowed) }
@FuncStub(PaymentsApiProtocol.buySubscriptionForZeroRequest, initialReturn: { SubscriptionRequest(api: $0.0, planId: $0.1) }) public var buySubscriptionForZeroRequestStub
public func buySubscriptionForZeroRequest(api: APIService, planId: String) -> SubscriptionRequest { buySubscriptionForZeroRequestStub(api, planId) }