Skip to content

Commit

Permalink
Expand PaymentMethod in consumers/payment_details/share request inste… (
Browse files Browse the repository at this point in the history
#3901)

…ad of using dummy empty PaymentMethod

## Motivation
https://jira.corp.stripe.com/browse/RUN_MOBILESDK-3405

## Testing
Added assert and UI Test - it's indirect, but combined they do test what
we want. Directly testing this seems very hard, since IDK how to
simulate the Link signup flow outside a UI test.

## Changelog
* [Fixed] Fixed an issue where signing up with Link and paying would
vend an empty `STPPaymentMethod` object to an `IntentConfiguration`
confirmHandler callback.
  • Loading branch information
yuki-stripe authored Aug 9, 2024
1 parent 8a6ad0e commit 5662dea
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 18 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
## X.Y.Z 2024-xx-yy
## X.Y.Z 2024-XX-YY
### PaymentSheet
* [Fixed] Fixed an issue where signing up with Link and paying would vend an empty `STPPaymentMethod` object to an `IntentConfiguration` confirmHandler callback.
* [Fixed] Fixed PaymentSheet.FlowController returning unlocalized labels for certain payment methods e.g. "AfterPay ClearPay" instead of "Afterpay" or "Clearpay" depending on locale.

### PaymentsUI
* [Fixed] Fixed an issue where STPPaymentCardTextField wouldn't call its delegate `paymentCardTextFieldDidChange` method when the preferred card network changed.


## 23.29.0 2024-08-05
### PaymentSheet
* [Fixed] Fixed a scroll issue with native 3DS2 authentication screen when the keyboard appears.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,11 @@ extension PlaygroundController {
func confirmHandler(_ paymentMethod: STPPaymentMethod,
_ shouldSavePaymentMethod: Bool,
_ intentCreationCallback: @escaping (Result<String, Error>) -> Void) {
// Sanity check the payment method
if paymentMethod.type == .card {
assert(paymentMethod.card != nil)
}

switch settings.integrationType {
case .deferred_mp:
// multiprocessor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,17 @@ class PaymentSheetLinkUITests: PaymentSheetUITestCase {

XCTAssertTrue(app.staticTexts["Success!"].waitForExistence(timeout: 10.0))
}

func testLinkInlineSignup_deferred() {
var settings = PaymentSheetTestPlaygroundSettings.defaultValues()
settings.customerMode = .guest
settings.apmsEnabled = .on
settings.linkEnabled = .on
settings.integrationType = .deferred_ssc
loadPlayground(app, settings)
app.buttons["Present PaymentSheet"].waitForExistenceAndTap()
fillLinkAndPay(mode: .checkbox)
}

// MARK: Link test helpers

Expand Down Expand Up @@ -2266,6 +2277,11 @@ class PaymentSheetLinkUITests: PaymentSheetUITestCase {
app.buttons["Confirm"].waitForExistenceAndTap()
}
XCTAssertTrue(app.staticTexts["Success!"].waitForExistence(timeout: 10.0))
// Roundabout way to validate that signup completed successfully
let signupCompleteAnalytic = analyticsLog.first { payload in
payload["event"] as? String == "link.signup.complete"
}
XCTAssertNotNil(signupCompleteAnalytic)
}

private func assertLinkInlineSignupNotShown() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
316B33122B5F171C0008D2E5 /* UserDefaults+StripePaymentSheetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316B33112B5F171C0008D2E5 /* UserDefaults+StripePaymentSheetTest.swift */; };
31AD3BE72B0C2D080080C800 /* UIApplication+StripePaymentSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31AD3BE62B0C2D080080C800 /* UIApplication+StripePaymentSheet.swift */; };
31CDFC362BA8E66200B3DD91 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 31CDFC352BA8E66200B3DD91 /* PrivacyInfo.xcprivacy */; };
31D224F42B4637BA003E3D8B /* PaymentDetailsShareResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D224F32B4637BA003E3D8B /* PaymentDetailsShareResponse.swift */; };
335A19D93A5979557DB4CA4D /* PaymentMethodElementWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5012364ED0F2EEC6EC2AB52 /* PaymentMethodElementWrapper.swift */; };
34CF08CBC636F596B8BA4C12 /* TextFieldElement+CardTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7F5905759525C9BF8BD4 /* TextFieldElement+CardTest.swift */; };
367BB57FA826A82EEF074A70 /* PayWithLinkWebController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617C44F9338DE2E93E318291 /* PayWithLinkWebController.swift */; };
Expand Down Expand Up @@ -217,6 +216,7 @@
B64FEF122C0FAC1E00F7CA26 /* PaymentSheetVerticalViewControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64FEF112C0FAC1E00F7CA26 /* PaymentSheetVerticalViewControllerTest.swift */; };
B65B42972C013DED00EC565D /* PaymentMethodFormViewControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B42962C013DED00EC565D /* PaymentMethodFormViewControllerTest.swift */; };
B65FE7092BED33EA009A73FC /* VerticalPaymentMethodListViewControllerSnapshotTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65FE7082BED33EA009A73FC /* VerticalPaymentMethodListViewControllerSnapshotTest.swift */; };
B662953E2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B662953D2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift */; };
B667BF0B2BF2B7C60050EFD8 /* RowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B667BF0A2BF2B7C60050EFD8 /* RowButton.swift */; };
B66D70FA2C54086600DECB3D /* HostedSurface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66D70F92C54086600DECB3D /* HostedSurface.swift */; };
B67D01B62C46FE9900ED8172 /* CVCReconfirmationVerticalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B67D01B52C46FE9900ED8172 /* CVCReconfirmationVerticalViewController.swift */; };
Expand Down Expand Up @@ -398,7 +398,6 @@
316B33112B5F171C0008D2E5 /* UserDefaults+StripePaymentSheetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+StripePaymentSheetTest.swift"; sourceTree = "<group>"; };
31AD3BE62B0C2D080080C800 /* UIApplication+StripePaymentSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIApplication+StripePaymentSheet.swift"; path = "StripePaymentSheet/Source/Categories/UIApplication+StripePaymentSheet.swift"; sourceTree = "<group>"; };
31CDFC352BA8E66200B3DD91 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
31D224F32B4637BA003E3D8B /* PaymentDetailsShareResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentDetailsShareResponse.swift; sourceTree = "<group>"; };
32332E0DB0AE12377EBDDEF1 /* PaymentSheetIntentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetIntentConfiguration.swift; sourceTree = "<group>"; };
32BDC53A88FB17F378C6B413 /* CardSectionWithScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardSectionWithScannerView.swift; sourceTree = "<group>"; };
33B8F21A22FA091BC9D2924B /* LinkStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkStubs.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -574,6 +573,7 @@
B64FEF112C0FAC1E00F7CA26 /* PaymentSheetVerticalViewControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetVerticalViewControllerTest.swift; sourceTree = "<group>"; };
B65B42962C013DED00EC565D /* PaymentMethodFormViewControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodFormViewControllerTest.swift; sourceTree = "<group>"; };
B65FE7082BED33EA009A73FC /* VerticalPaymentMethodListViewControllerSnapshotTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalPaymentMethodListViewControllerSnapshotTest.swift; sourceTree = "<group>"; };
B662953D2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentDetailsShareResponse.swift; sourceTree = "<group>"; };
B667BF0A2BF2B7C60050EFD8 /* RowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowButton.swift; sourceTree = "<group>"; };
B667E074D30964FABC64B552 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
B66D70F92C54086600DECB3D /* HostedSurface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostedSurface.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -921,8 +921,8 @@
4208AD2E0A737F5E0F00DE48 /* ConsumerSession+LookupResponse.swift */,
E2D61B52BFA201D25E8F6428 /* ConsumerSession+PublishableKey.swift */,
981F958E99945A0318D47BBF /* PaymentDetails.swift */,
31D224F32B4637BA003E3D8B /* PaymentDetailsShareResponse.swift */,
F1E614E8481658A027599A92 /* STPAPIClient+Link.swift */,
B662953D2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift */,
441C3414745D483C9A47ED0B /* VerificationSession.swift */,
);
path = Link;
Expand Down Expand Up @@ -1816,7 +1816,6 @@
B67D01B62C46FE9900ED8172 /* CVCReconfirmationVerticalViewController.swift in Sources */,
47B19F96CCEA290541E3B988 /* CardSectionElement.swift in Sources */,
04FEA90F2D0CB9D1C2029D21 /* CardSectionWithScannerView.swift in Sources */,
31D224F42B4637BA003E3D8B /* PaymentDetailsShareResponse.swift in Sources */,
6BA8D3342B0C1F79008C51FF /* CVCRecollectionElement.swift in Sources */,
6BC19CCC2B16C4B2008E00C4 /* CVCRecollectionView.swift in Sources */,
68E3CF21A7E1525CA05BA260 /* ConnectionsElement.swift in Sources */,
Expand Down Expand Up @@ -1896,6 +1895,7 @@
436A212E364FD78C3745DDA3 /* CardScanningView.swift in Sources */,
19A6D9D9951E13377F305263 /* CircularButton.swift in Sources */,
6BA8D33A2B0C1FBF008C51FF /* CVCPaymentMethodInformationView.swift in Sources */,
B662953E2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift in Sources */,
F79DBDF42E5C0ED6B6DDC246 /* ConfirmButton.swift in Sources */,
5C0D1B932954D0EF3F3A679F /* ManualEntryButton.swift in Sources */,
3DE056395324C5B3A5AAABDA /* PayWithLinkButton.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,32 @@
// PaymentDetailsShareResponse.swift
// StripePaymentSheet
//
// Created by David Estes on 1/3/24.
// Created by Yuki Tokuhiro on 8/7/24.
//

import Foundation
@_spi(STP) import StripePayments

struct PaymentDetailsShareResponse: Decodable {
let paymentMethod: String
final class PaymentDetailsShareResponse: NSObject, STPAPIResponseDecodable {
static func decodedObject(fromAPIResponse response: [AnyHashable: Any]?) -> Self? {
guard
let response,
let paymentMethodDict = response["payment_method"] as? [String: Any],
let paymentMethod = STPPaymentMethod.decodedObject(fromAPIResponse: paymentMethodDict)
else {
return nil
}
let paymentDetailsShareResponse = PaymentDetailsShareResponse(
paymentMethod: paymentMethod,
allResponseFields: response
)
return .some(paymentDetailsShareResponse as! Self)
}

let allResponseFields: [AnyHashable: Any]
let paymentMethod: STPPaymentMethod
init(paymentMethod: STPPaymentMethod, allResponseFields: [AnyHashable: Any]) {
self.paymentMethod = paymentMethod
self.allResponseFields = allResponseFields
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,26 @@ extension STPAPIClient {
var parameters: [String: Any] = [
"credentials": ["consumer_session_client_secret": consumerSessionClientSecret],
"request_surface": "ios_payment_element",
"expand": ["payment_method"],
"id": id,
]

if let cvc = cvc {
parameters["payment_method_options"] = ["card": ["cvc": cvc]]
}

post(
resource: endpoint,
parameters: parameters,
ephemeralKeySecret: nil,
completion: completion
)
APIRequest<PaymentDetailsShareResponse>.post(
with: self,
endpoint: endpoint,
parameters: parameters
) { paymentDetailsShareResponse, _, error in
guard let paymentDetailsShareResponse else {
stpAssert(error != nil)
completion(.failure(error ?? NSError.stp_genericConnectionError()))
return
}
completion(.success(paymentDetailsShareResponse))
}
}

func logout(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,8 @@ extension PaymentSheet {
// If passthrough mode, share payment details
linkAccount.sharePaymentDetails(id: paymentDetails.stripeID, cvc: paymentMethodParams.card?.cvc) { result in
switch result {
case .success(let shareResponse):
confirmWithPaymentMethod(STPPaymentMethod(stripeId: shareResponse.paymentMethod, type: .card), linkAccount, shouldSave)
case .success(let paymentDetailsShareResponse):
confirmWithPaymentMethod(paymentDetailsShareResponse.paymentMethod, linkAccount, shouldSave)
case .failure(let error):
STPAnalyticsClient.sharedClient.logLinkSharePaymentDetailsFailure(error: error)
// If this fails, confirm directly
Expand Down Expand Up @@ -447,7 +447,6 @@ extension PaymentSheet {
switch result {
case .success:
STPAnalyticsClient.sharedClient.logLinkSignupComplete()

createPaymentDetailsAndConfirm(linkAccount, intentConfirmParams.paymentMethodParams, intentConfirmParams.saveForFutureUseCheckboxState == .selected)
case .failure(let error as NSError):
STPAnalyticsClient.sharedClient.logLinkSignupFailure(error: error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,6 @@ extension STPAPIClient {
) { paymentMethod, _, error in
completion(paymentMethod, error)
}

}

/// Creates a PaymentMethod object with the provided params object.
Expand Down

0 comments on commit 5662dea

Please sign in to comment.