Skip to content

Commit

Permalink
Adding UPI integration for iOS (#13)
Browse files Browse the repository at this point in the history
* Adding UPI integration

* project.pbxproj changes

* project.pbxproj changes

* fix linting tests

* making vpa non-optional

* change Upi to UPI

* fix name in comments

Co-authored-by: David Estes <[email protected]>
  • Loading branch information
anirudh-stripe and davidme-stripe authored Nov 19, 2020
1 parent 80ed9dd commit 1b1b4ad
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 2 deletions.
16 changes: 16 additions & 0 deletions Stripe.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@
8BD87B901EFB17AA00269C2B /* STPSourceRedirectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD87B8F1EFB17AA00269C2B /* STPSourceRedirectTest.m */; };
8BD87B951EFB1CB100269C2B /* STPSourceVerificationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD87B941EFB1CB100269C2B /* STPSourceVerificationTest.m */; };
8BE5AE8B1EF8905B0081A33C /* STPCardParamsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BE5AE8A1EF8905B0081A33C /* STPCardParamsTest.m */; };
8F5ED77925551F2D003BE002 /* STPPaymentMethodUPIParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5ED77825551F2D003BE002 /* STPPaymentMethodUPIParams.swift */; };
8F5ED78325552296003BE002 /* STPPaymentMethodUPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5ED78225552296003BE002 /* STPPaymentMethodUPI.swift */; };
8F5ED78F2555528F003BE002 /* STPPaymentMethodUPIParamsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F5ED78E2555528F003BE002 /* STPPaymentMethodUPIParamsTest.m */; };
8FF0FAFC255A6FBB00218730 /* STPPaymentMethodUPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF0FAFB255A6FBB00218730 /* STPPaymentMethodUPITests.swift */; };
B3302F462006FBA7005DDBE9 /* STPConnectAccountParamsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B3302F452006FBA7005DDBE9 /* STPConnectAccountParamsTest.m */; };
B36C6D782193A16F00D17575 /* STPIntentActionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B36C6D772193A16F00D17575 /* STPIntentActionTest.m */; };
B3BDCACF20EEF4640034F7F5 /* STPPaymentIntentFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B3BDCACE20EEF4640034F7F5 /* STPPaymentIntentFunctionalTest.m */; };
Expand Down Expand Up @@ -861,6 +865,10 @@
8BD87B8F1EFB17AA00269C2B /* STPSourceRedirectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceRedirectTest.m; sourceTree = "<group>"; };
8BD87B941EFB1CB100269C2B /* STPSourceVerificationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceVerificationTest.m; sourceTree = "<group>"; };
8BE5AE8A1EF8905B0081A33C /* STPCardParamsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPCardParamsTest.m; sourceTree = "<group>"; };
8F5ED77825551F2D003BE002 /* STPPaymentMethodUPIParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodUPIParams.swift; sourceTree = "<group>"; };
8F5ED78225552296003BE002 /* STPPaymentMethodUPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodUPI.swift; sourceTree = "<group>"; };
8F5ED78E2555528F003BE002 /* STPPaymentMethodUPIParamsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodUPIParamsTest.m; sourceTree = "<group>"; };
8FF0FAFB255A6FBB00218730 /* STPPaymentMethodUPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodUPITests.swift; sourceTree = "<group>"; };
B3302F452006FBA7005DDBE9 /* STPConnectAccountParamsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPConnectAccountParamsTest.m; sourceTree = "<group>"; };
B36C6D772193A16F00D17575 /* STPIntentActionTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPIntentActionTest.m; sourceTree = "<group>"; };
B3BDCACE20EEF4640034F7F5 /* STPPaymentIntentFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentIntentFunctionalTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1427,6 +1435,8 @@
317ABD9E25117C9900CC59EF /* STPPaymentMethodSofort.swift */,
317ABDD325117C9D00CC59EF /* STPPaymentMethodSofortParams.swift */,
317ABD2B25117C9300CC59EF /* STPPaymentMethodThreeDSecureUsage.swift */,
8F5ED78225552296003BE002 /* STPPaymentMethodUPI.swift */,
8F5ED77825551F2D003BE002 /* STPPaymentMethodUPIParams.swift */,
);
name = Types;
sourceTree = "<group>";
Expand Down Expand Up @@ -1697,6 +1707,8 @@
36B6CB56234BDC4400331C38 /* STPPaymentMethodSEPADebitTest.m */,
31C8644824DDF2550015F7DF /* STPPaymentMethodSofortParamsTests.m */,
3111C405252BC79F00207E32 /* STPPaymentMethodSofortTests.swift */,
8F5ED78E2555528F003BE002 /* STPPaymentMethodUPIParamsTest.m */,
8FF0FAFB255A6FBB00218730 /* STPPaymentMethodUPITests.swift */,
3111C39B252BC78E00207E32 /* STPPaymentMethodTest.swift */,
B66D5023222F5A27004A9210 /* STPPaymentMethodThreeDSecureUsageTest.m */,
3111C413252BC7A200207E32 /* STPPaymentOptionsViewControllerTest.swift */,
Expand Down Expand Up @@ -2039,6 +2051,7 @@
31C5B888252E9C7400A481A7 /* STPCustomerContextTest.m in Sources */,
C18867DC1E8B0C4100A77634 /* STPFixtures.m in Sources */,
3111C615252D275A00207E32 /* STPAddressViewModelTest.swift in Sources */,
8FF0FAFC255A6FBB00218730 /* STPPaymentMethodUPITests.swift in Sources */,
3111C5B0252BF41A00207E32 /* STPPushProvisioningDetailsFunctionalTest.swift in Sources */,
B66D5024222F5A27004A9210 /* STPPaymentMethodThreeDSecureUsageTest.m in Sources */,
C1CFCB761ED5E12400BE45DF /* STPFileFunctionalTest.m in Sources */,
Expand Down Expand Up @@ -2099,6 +2112,7 @@
316F814525411879000A80B5 /* STPPaymentMethodPayPalTests.m in Sources */,
3111C5C0252BF8EB00207E32 /* STPMandateDataParamsTest.swift in Sources */,
3111C640252D40F100207E32 /* STPCardBINMetadataTests.swift in Sources */,
8F5ED78F2555528F003BE002 /* STPPaymentMethodUPIParamsTest.m in Sources */,
3111C600252D1FCB00207E32 /* STPPaymentMethodAUBECSDebitTests.swift in Sources */,
3111C63D252D405400207E32 /* STPAUBECSDebitFormViewSnapshotTests.swift in Sources */,
3111C652252D42D200207E32 /* STPTestingAPIClient.m in Sources */,
Expand Down Expand Up @@ -2177,6 +2191,7 @@
files = (
317ABF3425118C1E00CC59EF /* STPCardScanner.swift in Sources */,
3176C233251A6B0600300ADE /* STPThreeDSLabelCustomization.swift in Sources */,
8F5ED78325552296003BE002 /* STPPaymentMethodUPI.swift in Sources */,
3176C246251A7F6500300ADE /* STPPaymentOptionTableViewCell.swift in Sources */,
317ABF5A2512957800CC59EF /* STPBECSDebitAccountNumberValidator.swift in Sources */,
312D789B253DF824009224AF /* STPLocalizedString.swift in Sources */,
Expand Down Expand Up @@ -2348,6 +2363,7 @@
B64519C6251410EA006BF25E /* STPPaymentMethodAUBECSDebitParams.swift in Sources */,
31D4D6762512E73700809066 /* UIImage+Stripe.swift in Sources */,
B672432B2524E6CE002E1AAF /* STPPaymentMethodSofort.swift in Sources */,
8F5ED77925551F2D003BE002 /* STPPaymentMethodUPIParams.swift in Sources */,
B6926AD025265B5F001F208B /* STPCardParams.swift in Sources */,
B64519B72513FF4D006BF25E /* STPPaymentMethodAlipay.swift in Sources */,
B6926AF425268576001F208B /* STPFormEncodable.swift in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions Stripe/Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@
/* Default missing source type label */
"Unknown" = "Unknown";

/* Payment Method type brand name */
"UPI" = "UPI";

/* Button to fill shipping address from billing address. */
"Use Billing" = "Use Billing";

Expand Down
1 change: 1 addition & 0 deletions Stripe/STPPaymentHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL

case .alipay,
.card,
.UPI,
.iDEAL,
.FPX,
.cardPresent,
Expand Down
10 changes: 9 additions & 1 deletion Stripe/STPPaymentMethod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
@objc private(set) public var oxxo: STPPaymentMethodOXXO?
/// If this is a Sofort PaymentMethod (i.e. `self.type == STPPaymentMethodTypeSofort`), this contains additional details.
@objc private(set) public var sofort: STPPaymentMethodSofort?
/// If this is a UPI PaymentMethod (i.e. `self.type == STPPaymentMethodTypeUPI`), this contains additional details. :nodoc:
@objc private(set) public var upi: STPPaymentMethodUPI?
/// If this is a PayPal PaymentMethod (i.e. `self.type == STPPaymentMethodTypePayPal`), this contains additional details. :nodoc:
@objc private(set) public var payPal: STPPaymentMethodPayPal?
/// The ID of the Customer to which this PaymentMethod is saved. Nil when the PaymentMethod has not been saved to a Customer.
Expand Down Expand Up @@ -99,6 +101,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
"przelewy24 = \(String(describing: przelewy24))",
"sepaDebit = \(String(describing: sepaDebit))",
"sofort = \(String(describing: sofort))",
"upi = \(String(describing: upi))",
"liveMode = \(liveMode ? "YES" : "NO")",
"type = \(allResponseFields["type"] as? String ?? "")",
]
Expand All @@ -122,6 +125,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
"bancontact": NSNumber(value: STPPaymentMethodType.bancontact.rawValue),
"oxxo": NSNumber(value: STPPaymentMethodType.OXXO.rawValue),
"sofort": NSNumber(value: STPPaymentMethodType.sofort.rawValue),
"upi": NSNumber(value: STPPaymentMethodType.UPI.rawValue),
"alipay": NSNumber(value: STPPaymentMethodType.alipay.rawValue),
"paypal": NSNumber(value: STPPaymentMethodType.payPal.rawValue),
]
Expand Down Expand Up @@ -215,6 +219,8 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
fromAPIResponse: dict.stp_dictionary(forKey: "oxxo"))
paymentMethod.sofort = STPPaymentMethodSofort.decodedObject(
fromAPIResponse: dict.stp_dictionary(forKey: "sofort"))
paymentMethod.upi = STPPaymentMethodUPI.decodedObject(
fromAPIResponse: dict.stp_dictionary(forKey: "upi"))
paymentMethod.customerId = dict.stp_string(forKey: "customer")
paymentMethod.alipay = STPPaymentMethodAlipay.decodedObject(
fromAPIResponse: dict.stp_dictionary(forKey: "alipay"))
Expand Down Expand Up @@ -279,6 +285,8 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
return STPLocalizedString("OXXO", "Payment Method type brand name")
case .sofort:
return STPLocalizedString("Sofort", "Payment Method type brand name")
case .UPI:
return STPLocalizedString("UPI", "Payment Method type brand name")
case .payPal:
return STPLocalizedString("PayPal", "Payment Method type brand name")
case .bacsDebit, .cardPresent, // fall through
Expand All @@ -295,7 +303,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti
return true
case .alipay /* Careful! Revisit this if/when we support recurring Alipay */, .AUBECSDebit,
.bacsDebit, .SEPADebit, .iDEAL, .FPX, .cardPresent, .giropay, .EPS, .payPal, .przelewy24, .bancontact,
.OXXO, .sofort, .grabPay, // fall through
.OXXO, .sofort, .grabPay, .UPI, // fall through
.unknown:
return false
@unknown default:
Expand Down
2 changes: 2 additions & 0 deletions Stripe/STPPaymentMethodEnums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import Foundation
@objc(STPPaymentMethodTypeOXXO) case OXXO
/// A Sofort payment method.
case sofort
/// A UPI payment method.
case UPI
/// A PayPal payment method. :nodoc:
case payPal
/// An unknown type.
Expand Down
45 changes: 44 additions & 1 deletion Stripe/STPPaymentMethodParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
@objc public var oxxo: STPPaymentMethodOXXOParams?
/// If this is a Sofort PaymentMethod, this contains additional details.
@objc public var sofort: STPPaymentMethodSofortParams?
/// If this is a UPI PaymentMethod, this contains additional details.
@objc public var upi: STPPaymentMethodUPIParams?
/// If this is a GrabPay PaymentMethod, this contains additional details.
@objc public var grabPay: STPPaymentMethodGrabPayParams?
/// Set of key-value pairs that you can attach to the PaymentMethod. This can be useful for storing additional information about the PaymentMethod in a structured format.
Expand Down Expand Up @@ -296,6 +298,24 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
self.billingDetails = billingDetails
self.metadata = metadata
}

/// Creates params for a UPI PaymentMethod;
/// - Parameters:
/// - upi: An object containing additional UPI details.
/// - billingDetails: An object containing the user's billing details.
/// - metadata: Additional information to attach to the PaymentMethod.
@objc
public convenience init(
upi: STPPaymentMethodUPIParams,
billingDetails: STPPaymentMethodBillingDetails?,
metadata: [String: String]?
) {
self.init()
self.type = .UPI
self.upi = upi
self.billingDetails = billingDetails
self.metadata = metadata
}

/// Creates params for an Alipay PaymentMethod.
/// - Parameters:
Expand Down Expand Up @@ -383,6 +403,11 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
let sofort = STPPaymentMethodSofortParams()
self.sofort = sofort
self.billingDetails = paymentMethod.billingDetails
case .UPI:
self.type = .UPI
let upi = STPPaymentMethodUPIParams()
self.upi = upi
self.billingDetails = paymentMethod.billingDetails
case .grabPay:
self.type = .grabPay
let grabpay = STPPaymentMethodGrabPayParams()
Expand Down Expand Up @@ -421,6 +446,7 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
NSStringFromSelector(#selector(getter:bancontact)): "bancontact",
NSStringFromSelector(#selector(getter:oxxo)): "oxxo",
NSStringFromSelector(#selector(getter:sofort)): "sofort",
NSStringFromSelector(#selector(getter:upi)): "upi",
NSStringFromSelector(#selector(getter:metadata)): "metadata",
]
}
Expand Down Expand Up @@ -484,6 +510,8 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
return "OXXO"
case .sofort:
return "Sofort"
case .UPI:
return "UPI"
case .grabPay:
return "GrabPay"
case .payPal:
Expand All @@ -500,7 +528,7 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio
case .card:
return true
case .alipay, .AUBECSDebit, .bacsDebit, .SEPADebit, .iDEAL, .FPX, .cardPresent, .giropay,
.grabPay, .EPS, .przelewy24, .bancontact, .OXXO, .payPal, .sofort, // fall through
.grabPay, .EPS, .przelewy24, .bancontact, .OXXO, .payPal, .sofort, .UPI, // fall through
.unknown:
return false
@unknown default:
Expand Down Expand Up @@ -699,6 +727,21 @@ extension STPPaymentMethodParams {
return STPPaymentMethodParams(
sofort: sofort, billingDetails: billingDetails, metadata: metadata)
}

/// Creates params for a UPI PaymentMethod;
/// - Parameters:
/// - upi: An object containing additional UPI details.
/// - billingDetails: An object containing the user's billing details. Note that `billingDetails.name` and `billingDetails.email` are required to save bank details from a UPI payment.
/// - metadata: Additional information to attach to the PaymentMethod.
@objc(paramsWithUPI:billingDetails:metadata:)
public class func paramsWith(
upi: STPPaymentMethodUPIParams,
billingDetails: STPPaymentMethodBillingDetails?,
metadata: [String: String]?
) -> STPPaymentMethodParams {
return STPPaymentMethodParams(
upi: upi, billingDetails: billingDetails, metadata: metadata)
}

/// Creates params for an Alipay PaymentMethod.
/// - Parameters:
Expand Down
51 changes: 51 additions & 0 deletions Stripe/STPPaymentMethodUPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// STPPaymentMethodUPI.swift
// StripeiOS
//
// Created by Anirudh Bhargava on 11/6/20.
// Copyright © 2020 Stripe, Inc. All rights reserved.
//

import Foundation

/// A UPI Payment Method.
/// - seealso: https://stripe.com/docs/api/payment_methods/object#payment_method_object-upi
public class STPPaymentMethodUPI: NSObject, STPAPIResponseDecodable {
@objc private(set) public var allResponseFields: [AnyHashable: Any] = [:]

/// Customer’s Virtual Payment Address (VPA).
@objc public private(set) var vpa: String

// MARK: - Description
/// :nodoc:
@objc public override var description: String {
let props = [
// Object
String(format: "%@: %p", NSStringFromClass(STPPaymentMethodUPI.self), self),
"vpa = \(vpa)",
]

return "<\(props.joined(separator: "; "))>"
}

// MARK: - STPAPIResponseDecodable
@objc
public class func decodedObject(fromAPIResponse response: [AnyHashable: Any]?) -> Self? {
guard let response = response else {
return nil
}
return self.init(dictionary: response)
}

required init?(dictionary dict: [AnyHashable: Any]) {
let nsDict = dict as NSDictionary
guard let vpa = nsDict.stp_string(forKey: "vpa") else {
return nil
}

self.vpa = vpa

super.init()
allResponseFields = dict
}
}
29 changes: 29 additions & 0 deletions Stripe/STPPaymentMethodUPIParams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// STPPaymentMethodUPIParams.swift
// StripeiOS
//
// Created by Anirudh Bhargava on 11/6/20.
// Copyright © 2020 Stripe, Inc. All rights reserved.
//

import Foundation

/// An object representing parameters used to create a UPI Payment Method
public class STPPaymentMethodUPIParams: NSObject, STPFormEncodable {
@objc public var additionalAPIParameters: [AnyHashable: Any] = [:]

/// Customer’s Virtual Payment Address (VPA). Required.
@objc public var vpa: String?

@objc
public class func rootObjectName() -> String? {
return "upi"
}

@objc
public class func propertyNamesToFormFieldNamesMapping() -> [String: String] {
return [
NSStringFromSelector(#selector(getter:vpa)): "vpa"
]
}
}
54 changes: 54 additions & 0 deletions Tests/Tests/STPPaymentMethodUPIParamsTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// STPPaymentMethodUPIParamsTest.m
// StripeiOS Tests
//
// Created by Anirudh Bhargava on 11/6/20.
// Copyright © 2020 Stripe, Inc. All rights reserved.
//

#import <XCTest/XCTest.h>

#import "STPTestingAPIClient.h"

@interface STPPaymentMethodUPIParamsTests : XCTestCase

@end

@implementation STPPaymentMethodUPIParamsTests

- (void)testCreateUPIPaymentMethod {
STPAPIClient *client = [[STPAPIClient alloc] initWithPublishableKey:STPTestingINPublishableKey];
STPPaymentMethodUPIParams *upiParams = [STPPaymentMethodUPIParams new];
upiParams.vpa = @"somevpa@hdfcbank";
STPPaymentMethodBillingDetails *billingDetails = [STPPaymentMethodBillingDetails new];
billingDetails.name = @"Jenny Rosen";

STPPaymentMethodParams *params = [STPPaymentMethodParams paramsWithUPI:upiParams
billingDetails:billingDetails
metadata:@{@"test_key": @"test_value"}];

XCTestExpectation *expectation = [self expectationWithDescription:@"Payment Method UPI create"];

[client createPaymentMethodWithParams:params
completion:^(STPPaymentMethod * _Nullable paymentMethod, NSError * _Nullable error) {
[expectation fulfill];

XCTAssertNil(error, @"Unexpected error creating UPI PaymentMethod: %@", error);
XCTAssertNotNil(paymentMethod, @"Failed to create UPI PaymentMethod");
XCTAssertNotNil(paymentMethod.stripeId, @"Missing stripeId");
XCTAssertNotNil(paymentMethod.created, @"Missing created");
XCTAssertFalse(paymentMethod.liveMode, @"Incorrect livemode");
XCTAssertEqual(paymentMethod.type, STPPaymentMethodTypeUPI, @"Incorrect PaymentMethod type");

// Billing Details
XCTAssertEqualObjects(paymentMethod.billingDetails.name, @"Jenny Rosen", @"Incorrect name");

// UPI Details
XCTAssertNotNil(paymentMethod.upi, @"Missing UPI");
XCTAssertEqualObjects(paymentMethod.upi.vpa, @"somevpa@hdfcbank", @"Incorrect vpa");
}];

[self waitForExpectationsWithTimeout:STPTestingNetworkRequestTimeout handler:nil];
}

@end
Loading

0 comments on commit 1b1b4ad

Please sign in to comment.