Add initial support for Klarna (#1444)

This commit is contained in:
davidme-stripe 2019-12-03 14:25:36 -08:00 committed by GitHub
parent dd7d754219
commit 66935da611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 747 additions and 10 deletions

View File

@ -17,6 +17,7 @@
04533F191A688A0A00C7E52E /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 04533F181A688A0A00C7E52E /* Constants.m */; };
04D076171A69C11600094431 /* Stripe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 04533F0C1A68812D00C7E52E /* Stripe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
31A8934D230F6ABD007ABE37 /* FPXExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31A8934C230F6ABD007ABE37 /* FPXExampleViewController.m */; };
31C108D823677C0100FE91B7 /* KlarnaExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C108D723677C0100FE91B7 /* KlarnaExampleViewController.swift */; };
366F93B0225FF2A2005CFBF6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 366F93AF225FF2A2005CFBF6 /* README.md */; };
36B6CB5A234BE3FA00331C38 /* PaymentExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36B6CB59234BE3FA00331C38 /* PaymentExampleViewController.m */; };
36B6CB5D234BEB8400331C38 /* SEPADebitExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36B6CB5C234BEB8400331C38 /* SEPADebitExampleViewController.m */; };
@ -60,6 +61,7 @@
04533F181A688A0A00C7E52E /* Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = "<group>"; };
31A8934B230F6ABD007ABE37 /* FPXExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FPXExampleViewController.h; sourceTree = "<group>"; };
31A8934C230F6ABD007ABE37 /* FPXExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FPXExampleViewController.m; sourceTree = "<group>"; };
31C108D723677C0100FE91B7 /* KlarnaExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlarnaExampleViewController.swift; sourceTree = "<group>"; };
366F93AF225FF2A2005CFBF6 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = "Non-Card Payment Examples/README.md"; sourceTree = "<group>"; };
36B6CB58234BE3FA00331C38 /* PaymentExampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PaymentExampleViewController.h; sourceTree = "<group>"; };
36B6CB59234BE3FA00331C38 /* PaymentExampleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PaymentExampleViewController.m; sourceTree = "<group>"; };
@ -127,6 +129,7 @@
isa = PBXGroup;
children = (
B6C1FC822330432E0097FC4C /* AlipayExampleViewController.swift */,
31C108D723677C0100FE91B7 /* KlarnaExampleViewController.swift */,
04533E8E1A687F5D00C7E52E /* AppDelegate.h */,
04533E8F1A687F5D00C7E52E /* AppDelegate.m */,
C1CACE871E5DF7A9002D0821 /* ApplePayExampleViewController.h */,
@ -286,6 +289,7 @@
04533F191A688A0A00C7E52E /* Constants.m in Sources */,
C1CACE941E5E3DF6002D0821 /* SofortExampleViewController.m in Sources */,
04533EB21A68802E00C7E52E /* ShippingManager.m in Sources */,
31C108D823677C0100FE91B7 /* KlarnaExampleViewController.swift in Sources */,
04533E901A687F5D00C7E52E /* AppDelegate.m in Sources */,
B65E8FCC22FA078A0057E64A /* WeChatPayExampleViewController.m in Sources */,
36B6CB5A234BE3FA00331C38 /* PaymentExampleViewController.m in Sources */,

View File

@ -38,7 +38,7 @@
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 7;
return 8;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -65,6 +65,9 @@
case 6:
cell.textLabel.text = @"Alipay";
break;
case 7:
cell.textLabel.text = @"Klarna";
break;
}
return cell;
}
@ -72,48 +75,54 @@
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController *viewController;
switch (indexPath.row) {
case 1: {
case 0: {
ApplePayExampleViewController *exampleVC = [ApplePayExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 2: {
case 1: {
SofortExampleViewController *exampleVC = [SofortExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 3: {
case 2: {
WeChatPayExampleViewController *exampleVC = [WeChatPayExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 4: {
case 3: {
FPXExampleViewController *exampleVC = [FPXExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 5: {
case 4: {
SEPADebitExampleViewController *exampleVC = [SEPADebitExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 6: {
case 5: {
iDEALExampleViewController *exampleVC = [iDEALExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 7: {
case 6: {
AlipayExampleViewController *exampleVC = [AlipayExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
case 7: {
KlarnaExampleViewController *exampleVC = [KlarnaExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
}
[self.navigationController pushViewController:viewController animated:YES];
}

View File

@ -0,0 +1,167 @@
//
// KlarnaExampleViewController.swift
// Custom Integration
//
// Created by David Estes on 10/28/19.
// Copyright © 2019 Stripe. All rights reserved.
//
import UIKit
import Stripe
class KlarnaExampleViewController: UIViewController {
@objc weak var delegate: ExampleViewControllerDelegate?
var redirectContext: STPRedirectContext?
var inProgress: Bool = false {
didSet {
navigationController?.navigationBar.isUserInteractionEnabled = !inProgress
payWithAddressButton.isEnabled = !inProgress
payWithoutAddressButton.isEnabled = !inProgress
inProgress ? activityIndicatorView.startAnimating() : activityIndicatorView.stopAnimating()
}
}
// UI
lazy var activityIndicatorView = {
return UIActivityIndicatorView(style: .gray)
}()
lazy var payWithoutAddressButton: UIButton = {
let button = UIButton(type: .roundedRect)
button.setTitle("Pay with Klarna (Unknown Customer Address)", for: .normal)
button.addTarget(self, action: #selector(didTapPayButton), for: .touchUpInside)
return button
}()
lazy var payWithAddressButton: UIButton = {
let button = UIButton(type: .roundedRect)
button.setTitle("Pay with Klarna (Known Customer Address)", for: .normal)
button.addTarget(self, action: #selector(didTapPayButton), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Klarna"
[payWithAddressButton, payWithoutAddressButton, activityIndicatorView].forEach { subview in
view.addSubview(subview)
subview.translatesAutoresizingMaskIntoConstraints = false
}
let constraints = [
payWithAddressButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
payWithAddressButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
payWithoutAddressButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
payWithoutAddressButton.topAnchor.constraint(equalTo: payWithAddressButton.bottomAnchor),
activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
activityIndicatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
]
NSLayoutConstraint.activate(constraints)
}
@objc func didTapPayButton(sender: UIButton) {
guard Stripe.defaultPublishableKey() != nil else {
delegate?.exampleViewController(self, didFinishWithMessage: "Please set a Stripe Publishable Key in Constants.m")
return;
}
inProgress = true
if (sender == payWithAddressButton) {
payWithKnownCustomerInfo()
} else {
payWithoutCustomerInfo()
}
}
}
// MARK: -
extension KlarnaExampleViewController {
@objc func payWithKnownCustomerInfo() {
// You can optionally pass the customer's information to Klarna. If this information is not
// provided, Klarna will request it from the customer during checkout.
// Note: If you do not provide all required fields (full street address, first/last names, and
// email address), Stripe will not forward any address information to Klarna.
let address = STPAddress()
address.line1 = "29 Arlington Avenue"
address.email = "test@example.com"
address.city = "London"
address.postalCode = "N1 7BE"
address.country = "UK"
address.phone = "02012267709"
// Klarna requires separate first and last names for the customer.
let firstName = "Arthur"
let lastName = "Dent"
// In some EU countries, Klarna uses the user's date of birth for a credit check.
let dob = STPDateOfBirth.init()
dob.day = 11
dob.month = 3
dob.year = 1952
// Klarna requires individual line items in the transaction to be broken out
let items = [STPKlarnaLineItem(itemType: .SKU, itemDescription: "Towel", quantity: 1, totalAmount: 10000),
STPKlarnaLineItem(itemType: .SKU, itemDescription: "Digital Watch", quantity: 2, totalAmount: 20000),
STPKlarnaLineItem(itemType: .tax, itemDescription: "Taxes", quantity: 1, totalAmount: 100),
STPKlarnaLineItem(itemType: .shipping, itemDescription: "Shipping", quantity: 1, totalAmount: 100)]
let sourceParams = STPSourceParams.klarnaParams(
withReturnURL: "payments-example://stripe-redirect",
currency: "GBP",
purchaseCountry: "UK",
items: items,
customPaymentMethods: [], // The CustomPaymentMethods flag is ignored outside the US
billingAddress: address,
billingFirstName: firstName,
billingLastName: lastName,
billingDOB: dob)
// Klarna provides a wide variety of additional configuration options which you can use
// via the `additionalAPIParameters` field. See https://stripe.com/docs/sources/klarna for details.
if var additionalKlarnaParameters = sourceParams.additionalAPIParameters["klarna"] as? [String : Any] {
additionalKlarnaParameters["page_title"] = "Zoo";
sourceParams.additionalAPIParameters["klarna"] = additionalKlarnaParameters
}
payWithSourceParams(sourceParams: sourceParams)
}
@objc func payWithoutCustomerInfo() {
// This is the minimal amount of information required for a Klarna transaction.
// Klarna will request additional information from the customer during checkout.
let items = [STPKlarnaLineItem(itemType: .SKU, itemDescription: "Mysterious Item", quantity: 1, totalAmount: 10000)]
let sourceParams = STPSourceParams.klarnaParams(
withReturnURL: "payments-example://stripe-redirect",
currency: "USD",
purchaseCountry: "US",
items: items,
customPaymentMethods: [.installments, .payIn4])
payWithSourceParams(sourceParams: sourceParams)
}
@objc func payWithSourceParams(sourceParams: STPSourceParams) {
// 1. Create an Klarna Source.
STPAPIClient.shared().createSource(with: sourceParams) { source, error in
guard let source = source else {
self.delegate?.exampleViewController(self, didFinishWithError: error)
return
}
// 2. Redirect your customer to Klarna.
self.redirectContext = STPRedirectContext(source: source) { sourceID, clientSecret, error in
guard error == nil else {
self.delegate?.exampleViewController(self, didFinishWithError: error)
return
}
// 3. Poll your backend to show the customer their order status.
// This step is ommitted in the example, as our backend does not track orders.
self.delegate?.exampleViewController(self, didFinishWithMessage: "Your order was received and is awaiting payment confirmation.")
// 4. On your backend, use webhooks to charge the Source and fulfill the order
}
self.redirectContext?.startRedirectFlow(from: self)
}
}
}

View File

@ -367,6 +367,14 @@
0731329E2277ABF60019CE3F /* STPPinManagementService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0731328E2277A3F60019CE3F /* STPPinManagementService.m */; };
0731329F227CFE410019CE3F /* STPIssuingCardPin.h in Headers */ = {isa = PBXBuildFile; fileRef = 073132932277A72D0019CE3F /* STPIssuingCardPin.h */; settings = {ATTRIBUTES = (Public, ); }; };
3142E72722FCC19800DDA097 /* STPFPXBankBrand.h in Headers */ = {isa = PBXBuildFile; fileRef = 31F5B33322FCAC4000A71C64 /* STPFPXBankBrand.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A532384A713001FE708 /* STPKlarnaLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A512384A713001FE708 /* STPKlarnaLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A542384A713001FE708 /* STPKlarnaLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A512384A713001FE708 /* STPKlarnaLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A552384A713001FE708 /* STPKlarnaLineItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 314B6A522384A713001FE708 /* STPKlarnaLineItem.m */; };
314B6A562384A713001FE708 /* STPKlarnaLineItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 314B6A522384A713001FE708 /* STPKlarnaLineItem.m */; };
314B6A592384ABF9001FE708 /* STPSourceKlarnaDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A572384ABF9001FE708 /* STPSourceKlarnaDetails.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A5A2384ABF9001FE708 /* STPSourceKlarnaDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A572384ABF9001FE708 /* STPSourceKlarnaDetails.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A5B2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 314B6A582384ABF9001FE708 /* STPSourceKlarnaDetails.m */; };
314B6A5C2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 314B6A582384ABF9001FE708 /* STPSourceKlarnaDetails.m */; };
314F9CC0235E66920059E2F6 /* STPFPXBankStatusResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 314F9CBE235E66920059E2F6 /* STPFPXBankStatusResponse.h */; };
314F9CC1235E66920059E2F6 /* STPFPXBankStatusResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 314F9CBE235E66920059E2F6 /* STPFPXBankStatusResponse.h */; };
314F9CC2235E66920059E2F6 /* STPFPXBankStatusResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 314F9CBF235E66920059E2F6 /* STPFPXBankStatusResponse.m */; };
@ -1613,6 +1621,10 @@
3142E72A22FCC26600DDA097 /* stp_bank_fpx_public_bank@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_public_bank@2x.png"; sourceTree = "<group>"; };
3142E72B22FCC26600DDA097 /* stp_bank_fpx_rhb@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_rhb@2x.png"; sourceTree = "<group>"; };
3142E72C22FCC26600DDA097 /* stp_bank_fpx_maybank2e@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_maybank2e@2x.png"; sourceTree = "<group>"; };
314B6A512384A713001FE708 /* STPKlarnaLineItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = STPKlarnaLineItem.h; path = PublicHeaders/STPKlarnaLineItem.h; sourceTree = "<group>"; };
314B6A522384A713001FE708 /* STPKlarnaLineItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPKlarnaLineItem.m; sourceTree = "<group>"; };
314B6A572384ABF9001FE708 /* STPSourceKlarnaDetails.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = STPSourceKlarnaDetails.h; path = PublicHeaders/STPSourceKlarnaDetails.h; sourceTree = "<group>"; };
314B6A582384ABF9001FE708 /* STPSourceKlarnaDetails.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPSourceKlarnaDetails.m; sourceTree = "<group>"; };
314F9CBE235E66920059E2F6 /* STPFPXBankStatusResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPFPXBankStatusResponse.h; sourceTree = "<group>"; };
314F9CBF235E66920059E2F6 /* STPFPXBankStatusResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPFPXBankStatusResponse.m; sourceTree = "<group>"; };
315CB85222E7BD0D00E612A3 /* libStripe3DS2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libStripe3DS2.a; path = InternalFrameworks/libStripe3DS2.a; sourceTree = "<group>"; };
@ -3268,6 +3280,8 @@
F19491DD1E5F6B8C001E1FC2 /* STPSourceCardDetails.h */,
F19491D81E5F606F001E1FC2 /* STPSourceCardDetails.m */,
F1BEB2F81F34F2250043F48C /* STPSourceEnums.h */,
314B6A572384ABF9001FE708 /* STPSourceKlarnaDetails.h */,
314B6A582384ABF9001FE708 /* STPSourceKlarnaDetails.m */,
C1BD9B381E39416700CEE925 /* STPSourceOwner.h */,
C1BD9B271E39406C00CEE925 /* STPSourceOwner.m */,
C1D7B5181E36B8B9002181F5 /* STPSourceParams.h */,
@ -3285,6 +3299,8 @@
B68D52E222A739AA00D4E8BA /* STPSourceWeChatPayDetails.m */,
04CDB4CC1A5F30A700B854EE /* STPToken.h */,
04CDB4CD1A5F30A700B854EE /* STPToken.m */,
314B6A512384A713001FE708 /* STPKlarnaLineItem.h */,
314B6A522384A713001FE708 /* STPKlarnaLineItem.m */,
);
name = Models;
sourceTree = "<group>";
@ -3411,6 +3427,7 @@
B69CFB462236F8E3001E9885 /* STPPaymentMethodCardPresent.h in Headers */,
319A609C22E9186B00AACF66 /* STDSThreeDSProtocolVersion.h in Headers */,
04EBC75A1B7533C300A0E6AE /* STPCardValidator.h in Headers */,
314B6A542384A713001FE708 /* STPKlarnaLineItem.h in Headers */,
C159933D1D8808970047950D /* STPShippingMethodsViewController.h in Headers */,
F15232251EA9303800D65C67 /* STPURLCallbackHandler.h in Headers */,
B3A2413A1FFEB57400A2F00D /* STPConnectAccountParams.h in Headers */,
@ -3549,6 +3566,7 @@
04F94D9B1D229E76004FC826 /* STPTheme.h in Headers */,
04F213331BCEAB61001D6F22 /* STPFormEncodable.h in Headers */,
04F94D9C1D229EAA004FC826 /* PKPayment+Stripe.h in Headers */,
314B6A5A2384ABF9001FE708 /* STPSourceKlarnaDetails.h in Headers */,
04F94DCF1D22A234004FC826 /* NSDecimalNumber+Stripe_Currency.h in Headers */,
319A609722E9186B00AACF66 /* STDSRuntimeException.h in Headers */,
8BD87B931EFB1C1E00269C2B /* STPSourceVerification+Private.h in Headers */,
@ -3658,6 +3676,7 @@
B32B176520F80442000D6EF8 /* STPRedirectContext+Private.h in Headers */,
F19491DE1E5F6B8C001E1FC2 /* STPSourceCardDetails.h in Headers */,
0439B9871C454F97005A1ED5 /* STPPaymentOptionsViewController.h in Headers */,
314B6A532384A713001FE708 /* STPKlarnaLineItem.h in Headers */,
04B31DF91D11AC6400EF1631 /* STPUserInformation.h in Headers */,
31F5A50922F0EFB10033663B /* STPPaymentMethodFPXParams.h in Headers */,
04EBC7531B7533C300A0E6AE /* STPCardValidationState.h in Headers */,
@ -3859,6 +3878,7 @@
3621DDCC22A5E4FD00281BC4 /* STPThreeDSCustomizationSettings.h in Headers */,
B6B41F7F22348A1E0020BA7F /* STPPaymentMethodiDEALParams.h in Headers */,
B69FEC42222EE9E000273A16 /* STPPaymentMethod.h in Headers */,
314B6A592384ABF9001FE708 /* STPSourceKlarnaDetails.h in Headers */,
B6794A4922F4B5CC00E3AB41 /* STPConnectAccountCompanyParams.h in Headers */,
04793F561D1D8DDD00B3C551 /* STPSourceProtocol.h in Headers */,
0451CC441C49AE1C003B2CA6 /* STPPaymentResult.h in Headers */,
@ -4769,7 +4789,9 @@
045D71231CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m in Sources */,
36B6CB7C2359052D00331C38 /* STPMandateDataParams.m in Sources */,
04BC29A11CD8412000318357 /* STPPaymentContext.m in Sources */,
314B6A562384A713001FE708 /* STPKlarnaLineItem.m in Sources */,
04CDE5C71BC20AF800548833 /* STPBankAccountParams.m in Sources */,
314B6A5C2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */,
C1363BBA1D7633D800EB82B4 /* STPPaymentOptionTableViewCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -4822,6 +4844,7 @@
04B31DFB1D11AC6400EF1631 /* STPUserInformation.m in Sources */,
F12829DC1D7747E4008B10D6 /* STPBundleLocator.m in Sources */,
04BC29BE1CDD535700318357 /* STPSwitchTableViewCell.m in Sources */,
314B6A5B2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */,
F1DEB88C1E2047CA0066B8E8 /* STPCoreTableViewController.m in Sources */,
04E39F6B1CED48D500AF3B96 /* UIBarButtonItem+Stripe.m in Sources */,
365BE89E2285F6080068D824 /* STPPaymentHandler.m in Sources */,
@ -4911,6 +4934,7 @@
B690DDEE222F01BF000B902D /* STPPaymentMethodBillingDetails.m in Sources */,
0438EF3B1B7416BB00D506CC /* STPPaymentCardTextFieldViewModel.m in Sources */,
C1BD9B301E3940A200CEE925 /* STPSourceRedirect.m in Sources */,
314B6A552384A713001FE708 /* STPKlarnaLineItem.m in Sources */,
04CDE5B81BC1F1F100548833 /* STPCardParams.m in Sources */,
B6CF3135229D8C3600BA8AC2 /* STPCardBrand.m in Sources */,
31F5A50F22F0EFDC0033663B /* STPPaymentMethodFPX.m in Sources */,

View File

@ -0,0 +1,74 @@
//
// STPKlarnaLineItem.h
// Stripe
//
// Created by David Estes on 11/19/19.
// Copyright © 2019 Stripe, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
The type of the Klarna line item.
*/
typedef NS_ENUM(NSUInteger, STPKlarnaLineItemType) {
/**
The line item for a product
*/
STPKlarnaLineItemTypeSKU,
/**
The line item for taxes
*/
STPKlarnaLineItemTypeTax,
/**
The line item for shipping costs
*/
STPKlarnaLineItemTypeShipping
};
/**
An object representing a line item in a Klarna source.
@see https://stripe.com/docs/sources/klarna#create-source
*/
@interface STPKlarnaLineItem : NSObject
/**
The line item's type. One of `sku` (for a product), `tax` (for taxes), or `shipping` (for shipping costs).
*/
@property (nonatomic) STPKlarnaLineItemType itemType;
/**
The human-readable description for the line item.
*/
@property (nonatomic, copy) NSString *itemDescription;
/**
The quantity to display for this line item.
*/
@property (nonatomic, copy) NSNumber *quantity;
/**
The total price of this line item.
Note: This is the total price after multiplying by the quantity, not
the price of an individual item. It is denominated in the currency
of the STPSourceParams which contains it.
*/
@property (nonatomic, copy) NSNumber *totalAmount;
/**
Initialize this `STPKlarnaLineItem` with a set of parameters.
@param itemType The line item's type.
@param itemDescription The human-readable description for the line item.
@param quantity The quantity to display for this line item.
@param totalAmount The total price of this line item.
*/
- (instancetype)initWithItemType:(STPKlarnaLineItemType)itemType itemDescription:(NSString *)itemDescription quantity:(NSNumber *)quantity totalAmount:(NSNumber *)totalAmount;
@end
NS_ASSUME_NONNULL_END

View File

@ -20,7 +20,7 @@
NS_ASSUME_NONNULL_BEGIN
@class STPSourceOwner, STPSourceReceiver, STPSourceRedirect, STPSourceVerification, STPSourceWeChatPayDetails;
@class STPSourceOwner, STPSourceReceiver, STPSourceRedirect, STPSourceVerification, STPSourceKlarnaDetails, STPSourceWeChatPayDetails;
/**
Representation of a customer's payment instrument created with the Stripe API. @see https://stripe.com/docs/api#sources
@ -120,6 +120,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, nullable, readonly) STPSourceCardDetails *cardDetails;
/**
If this is a Klarna source, this property provides typed access to the
contents of the `details` dictionary.
*/
@property (nonatomic, nullable, readonly) STPSourceKlarnaDetails *klarnaDetails;
/**
If this is a SEPA Debit source, this property provides typed access to the
contents of the `details` dictionary.

View File

@ -162,9 +162,35 @@ typedef NS_ENUM(NSInteger, STPSourceType) {
A WeChat Pay source. @see https://stripe.com/docs/sources/wechat-pay
*/
STPSourceTypeWeChatPay,
/**
A Klarna source. @see https://stripe.com/docs/sources/klarna
*/
STPSourceTypeKlarna,
/**
An unknown type of source.
*/
STPSourceTypeUnknown,
};
/**
Custom payment methods for Klarna
@see https://stripe.com/docs/sources/klarna#create-source
*/
typedef NS_OPTIONS(NSUInteger, STPKlarnaPaymentMethods) {
/**
Don't specify any custom payment methods.
*/
STPKlarnaPaymentMethodsNone = 0,
/**
Offer payments over 4 installments. (a.k.a. Pay Later in 4)
*/
STPKlarnaPaymentMethodsPayIn4 = 1 << 0,
/**
Offer payments over an arbitrary number of installments. (a.k.a. Slice It)
*/
STPKlarnaPaymentMethodsInstallments = 1 << 1,
};

View File

@ -0,0 +1,34 @@
//
// STPSourceKlarnaDetails.h
// Stripe
//
// Created by David Estes on 11/19/19.
// Copyright © 2019 Stripe, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "STPAPIResponseDecodable.h"
NS_ASSUME_NONNULL_BEGIN
/**
Details of a Klarna source.
*/
@interface STPSourceKlarnaDetails : NSObject <STPAPIResponseDecodable>
/**
The Klarna-specific client token. This may be used with the Klarna SDK.
@see https://developers.klarna.com/documentation/in-app/ios/steps-klarna-payments-native/#initialization
*/
@property (nonatomic, readonly) NSString *clientToken;
/**
The ISO-3166 2-letter country code of the customer's location.
*/
@property (nonatomic, readonly) NSString *purchaseCountry;
@end
NS_ASSUME_NONNULL_END

View File

@ -13,7 +13,7 @@
NS_ASSUME_NONNULL_BEGIN
@class STPCardParams;
@class STPCardParams, STPDateOfBirth, STPKlarnaLineItem;
/**
An object representing parameters used to create a Source object.
@ -208,6 +208,66 @@ NS_ASSUME_NONNULL_BEGIN
country:(NSString *)country
statementDescriptor:(nullable NSString *)statementDescriptor;
/**
Creates params for a Klarna source.
@see https://stripe.com/docs/sources/klarna#create-source
@param returnURL The URL the customer should be redirected to after
they have successfully verified the payment.
@param currency The currency the payment is being created in.
@param purchaseCountry The ISO-3166 2-letter country code of the customer's location.
@param items An array of STPKlarnaLineItems. Klarna will present these on the confirmation
page. The total amount charged will be a sum of the `totalAmount` of each of these items.
@param customPaymentMethods Required for customers located in the US. This determines whether Pay Later and/or Slice It
is offered to a US customer.
@param address An STPAddress for the customer. At a minimum, an `email`, `line1`, `postalCode`, `city`, and `country` must be provided.
The address' `name` will be ignored in favor of the `firstName and `lastName` parameters.
@param firstName The customer's first name.
@param lastName The customer's last name.
If the provided information is missing a line1, postal code, city, email, or first/last name, or if the country code is
outside the specified country, no address information will be sent to Klarna, and Klarna will prompt the customer to provide their address.
@param dateOfBirth The customer's date of birth. This will be used by Klarna for a credit check in some EU countries.
The optional fields (address, firstName, lastName, and dateOfBirth) can be provided to skip Klarna's customer information form.
If this information is missing, Klarna will prompt the customer for these values during checkout.
Be careful with this option: If the provided information is invalid,
Klarna may reject the transaction without giving the customer a chance to correct it.
@return an STPSourceParams object populated with the provided values.
*/
+ (STPSourceParams *)klarnaParamsWithReturnURL:(NSString *)returnURL
currency:(NSString *)currency
purchaseCountry:(NSString *)purchaseCountry
items:(NSArray<STPKlarnaLineItem *> *)items
customPaymentMethods:(STPKlarnaPaymentMethods)customPaymentMethods
billingAddress:(nullable STPAddress *)address
billingFirstName:(nullable NSString *)firstName
billingLastName:(nullable NSString *)lastName
billingDOB:(nullable STPDateOfBirth *)dateOfBirth;
/**
Creates params for a Klarna source.
@see https://stripe.com/docs/sources/klarna#create-source
@param returnURL The URL the customer should be redirected to after
they have successfully verified the payment.
@param currency The currency the payment is being created in.
@param purchaseCountry The ISO-3166 2-letter country code of the customer's location.
@param items An array of STPKlarnaLineItems. Klarna will present these in the confirmation
dialog. The total amount charged will be a sum of the `totalAmount` of each of these items.
@param customPaymentMethods Required for customers located in the US. This determines whether Pay Later and/or Slice It
is offered to a US customer.
@return an STPSourceParams object populated with the provided values.
*/
+ (STPSourceParams *)klarnaParamsWithReturnURL:(NSString *)returnURL
currency:(NSString *)currency
purchaseCountry:(NSString *)purchaseCountry
items:(NSArray<STPKlarnaLineItem *> *)items
customPaymentMethods:(STPKlarnaPaymentMethods)customPaymentMethods;
/**
Creates params for a 3DS source.
@see https://stripe.com/docs/sources/three-d-secure#create-3ds-source

View File

@ -44,6 +44,7 @@
#import "STPImageLibrary.h"
#import "STPIntentAction.h"
#import "STPIntentActionRedirectToURL.h"
#import "STPKlarnaLineItem.h"
#import "STPMandateCustomerAcceptanceParams.h"
#import "STPMandateDataParams.h"
#import "STPMandateOnlineParams.h"
@ -93,6 +94,7 @@
#import "STPSource.h"
#import "STPSourceCardDetails.h"
#import "STPSourceEnums.h"
#import "STPSourceKlarnaDetails.h"
#import "STPSourceOwner.h"
#import "STPSourceParams.h"
#import "STPSourceProtocol.h"

View File

@ -0,0 +1,24 @@
//
// STPKlarnaLineItem.m
// Stripe
//
// Created by David Estes on 11/19/19.
// Copyright © 2019 Stripe, Inc. All rights reserved.
//
#import "STPKlarnaLineItem.h"
@implementation STPKlarnaLineItem
- (instancetype)initWithItemType:(STPKlarnaLineItemType)itemType itemDescription:(NSString *)itemDescription quantity:(NSNumber *)quantity totalAmount:(NSNumber *)totalAmount {
self = [self init];
if (self) {
self.itemType = itemType;
self.itemDescription = itemDescription;
self.quantity = quantity;
self.totalAmount = totalAmount;
}
return self;
}
@end

View File

@ -13,6 +13,7 @@
#import "STPImageLibrary.h"
#import "STPLocalizationUtils.h"
#import "STPSourceOwner.h"
#import "STPSourceKlarnaDetails.h"
#import "STPSourceReceiver.h"
#import "STPSourceRedirect.h"
#import "STPSourceVerification.h"
@ -39,6 +40,7 @@
@property (nonatomic, nullable) STPSourceVerification *verification;
@property (nonatomic, nullable) NSDictionary *details;
@property (nonatomic, nullable) STPSourceCardDetails *cardDetails;
@property (nonatomic, nullable) STPSourceKlarnaDetails *klarnaDetails;
@property (nonatomic, nullable) STPSourceSEPADebitDetails *sepaDebitDetails;
@property (nonatomic, nullable) STPSourceWeChatPayDetails *weChatPayDetails;
@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields;
@ -65,6 +67,7 @@
@"eps": @(STPSourceTypeEPS),
@"multibanco": @(STPSourceTypeMultibanco),
@"wechat": @(STPSourceTypeWeChatPay),
@"klarna": @(STPSourceTypeKlarna),
};
}
@ -265,6 +268,8 @@
source.sepaDebitDetails = [STPSourceSEPADebitDetails decodedObjectFromAPIResponse:source.details];
} else if (source.type == STPSourceTypeWeChatPay) {
source.weChatPayDetails = [STPSourceWeChatPayDetails decodedObjectFromAPIResponse:source.details];
} else if (source.type == STPSourceTypeKlarna) {
source.klarnaDetails = [STPSourceKlarnaDetails decodedObjectFromAPIResponse:source.details];
}
return source;
@ -319,6 +324,8 @@
return STPLocalizedString(@"Multibanco", @"Source type brand name");
case STPSourceTypeWeChatPay:
return STPLocalizedString(@"WeChat Pay", @"Source type brand name");
case STPSourceTypeKlarna:
return STPLocalizedString(@"Klarna", @"Source type brand name");
case STPSourceTypeUnknown:
return STPLocalizedString(@"Unknown", @"Default missing source type label");
}

View File

@ -0,0 +1,48 @@
//
// STPSourceKlarnaDetails.m
// Stripe
//
// Created by David Estes on 11/19/19.
// Copyright © 2019 Stripe, Inc. All rights reserved.
//
#import "STPSourceKlarnaDetails.h"
#import "NSDictionary+Stripe.h"
@interface STPSourceKlarnaDetails()
@property (nonatomic, copy, readwrite) NSString *clientToken;
@property (nonatomic, copy, readwrite) NSString *purchaseCountry;
@property (nonatomic, copy, readwrite) NSDictionary *allResponseFields;
@end
@implementation STPSourceKlarnaDetails
#pragma mark - Description
- (NSString *)description {
NSArray *props = @[
[NSString stringWithFormat:@"%@: %p", NSStringFromClass([self class]), self],
[NSString stringWithFormat:@"clientToken = %@", self.clientToken],
[NSString stringWithFormat:@"purchaseCountry = %@", self.purchaseCountry],
];
return [NSString stringWithFormat:@"<%@>", [props componentsJoinedByString:@"; "]];
}
#pragma mark - STPAPIResponseDecodable
+ (nullable instancetype)decodedObjectFromAPIResponse:(nullable NSDictionary *)response {
NSDictionary *dict = [response stp_dictionaryByRemovingNulls];
if (!dict) {
return nil;
}
STPSourceKlarnaDetails *details = [[self class] new];
details.clientToken = [dict stp_stringForKey:@"client_token"];
details.purchaseCountry = [dict stp_stringForKey:@"purchase_country"];
details.allResponseFields = dict;
return details;
}
@end

View File

@ -13,6 +13,7 @@
#import "STPCardParams.h"
#import "STPFormEncoder.h"
#import "STPSource+Private.h"
#import "STPKlarnaLineItem.h"
@interface STPSourceParams ()
@ -223,6 +224,114 @@
return params;
}
+ (STPSourceParams *)klarnaParamsWithReturnURL:(NSString *)returnURL
currency:(NSString *)currency
purchaseCountry:(NSString *)purchaseCountry
items:(NSArray<STPKlarnaLineItem *> *)items
customPaymentMethods:(STPKlarnaPaymentMethods)customPaymentMethods
billingAddress:(STPAddress *)address
billingFirstName:(NSString *)firstName
billingLastName:(NSString *)lastName
billingDOB:(STPDateOfBirth *)dateOfBirth {
STPSourceParams *params = [self new];
params.type = STPSourceTypeKlarna;
params.currency = currency;
params.redirect = @{ @"return_url": returnURL };
NSMutableDictionary *additionalAPIParameters = [NSMutableDictionary dictionary];
NSMutableDictionary *klarnaDict = [NSMutableDictionary dictionary];
klarnaDict[@"product"] = @"payment";
klarnaDict[@"purchase_country"] = purchaseCountry;
if (address) {
if ([address.country isEqualToString:purchaseCountry] &&
address.line1 != nil &&
address.postalCode != nil &&
address.city != nil &&
address.email != nil &&
firstName != nil &&
lastName != nil) {
klarnaDict[@"first_name"] = firstName;
klarnaDict[@"last_name"] = lastName;
NSMutableDictionary *ownerDict = [NSMutableDictionary dictionary];
NSMutableDictionary *addressDict = [NSMutableDictionary dictionary];
addressDict[@"line1"] = address.line1;
addressDict[@"line2"] = address.line2;
addressDict[@"city"] = address.city;
addressDict[@"state"] = address.state;
addressDict[@"postal_code"] = address.postalCode;
addressDict[@"country"] = address.country;
ownerDict[@"address"] = addressDict;
ownerDict[@"phone"] = address.phone;
ownerDict[@"email"] = address.email;
additionalAPIParameters[@"owner"] = ownerDict;
}
}
if (dateOfBirth) {
klarnaDict[@"owner_dob_day"] = [NSString stringWithFormat:@"%02ld", (long)dateOfBirth.day];
klarnaDict[@"owner_dob_month"] = [NSString stringWithFormat:@"%02ld", (long)dateOfBirth.month];
klarnaDict[@"owner_dob_year"] = [NSString stringWithFormat:@"%li", (long)dateOfBirth.year];
}
NSUInteger amount = 0;
NSMutableArray *sourceOrderItems = [NSMutableArray arrayWithCapacity:[items count]];
for (STPKlarnaLineItem *item in items) {
NSString *itemType = nil;
switch (item.itemType) {
case STPKlarnaLineItemTypeSKU:
itemType = @"sku";
break;
case STPKlarnaLineItemTypeTax:
itemType = @"tax";
break;
case STPKlarnaLineItemTypeShipping:
itemType = @"shipping";
break;
default:
break;
}
[sourceOrderItems addObject:@{
@"type": itemType,
@"description": item.itemDescription,
@"quantity": item.quantity,
@"amount": item.totalAmount,
@"currency": currency
}];
amount = [item.totalAmount unsignedIntValue] + amount;
}
params.amount = @(amount);
if (customPaymentMethods != STPKlarnaPaymentMethodsNone) {
NSMutableArray *customPaymentMethodsArray = [NSMutableArray array];
if (customPaymentMethods & STPKlarnaPaymentMethodsPayIn4) {
[customPaymentMethodsArray addObject:@"payin4"];
}
if (customPaymentMethods & STPKlarnaPaymentMethodsInstallments) {
[customPaymentMethodsArray addObject:@"installments"];
}
klarnaDict[@"custom_payment_methods"] = [customPaymentMethodsArray componentsJoinedByString:@","];
}
additionalAPIParameters[@"source_order"] = @{@"items" : sourceOrderItems};
additionalAPIParameters[@"klarna"] = klarnaDict;
additionalAPIParameters[@"flow"] = @"redirect";
params.additionalAPIParameters = additionalAPIParameters;
return params;
}
+ (STPSourceParams *)klarnaParamsWithReturnURL:(NSString *)returnURL
currency:(NSString *)currency
purchaseCountry:(NSString *)purchaseCountry
items:(NSArray<STPKlarnaLineItem *> *)items
customPaymentMethods:(STPKlarnaPaymentMethods)customPaymentMethods {
return [self klarnaParamsWithReturnURL:returnURL currency:currency purchaseCountry:purchaseCountry items:items customPaymentMethods:customPaymentMethods billingAddress:nil billingFirstName:nil billingLastName:nil billingDOB:nil];
}
+ (STPSourceParams *)threeDSecureParamsWithAmount:(NSUInteger)amount
currency:(NSString *)currency
returnURL:(NSString *)returnURL

View File

@ -551,6 +551,40 @@ static NSString *const apiKey = @"pk_test_vOo1umqsYxSrP5UXfOeL3ecm";
[self waitForExpectationsWithTimeout:5.0f handler:nil];
}
- (void)testCreateSource_klarna {
NSArray *lineItems = @[[[STPKlarnaLineItem alloc] initWithItemType:STPKlarnaLineItemTypeSKU itemDescription:@"Test Item" quantity:@(2) totalAmount:@(500)],
[[STPKlarnaLineItem alloc] initWithItemType:STPKlarnaLineItemTypeTax itemDescription:@"Tax" quantity:@(1) totalAmount:@(100)]];
STPAddress *address = [[STPAddress alloc] init];
address.line1 = @"29 Arlington Avenue";
address.email = @"test@example.com";
address.city = @"London";
address.postalCode = @"N1 7BE";
address.country = @"UK";
address.phone = @"02012267709";
STPDateOfBirth *dob = [[STPDateOfBirth alloc] init];
dob.day = 11;
dob.month = 3;
dob.year = 1952;
STPSourceParams *params = [STPSourceParams klarnaParamsWithReturnURL:@"https://shop.example.com/return" currency:@"GBP" purchaseCountry:@"UK" items:lineItems customPaymentMethods:STPKlarnaPaymentMethodsNone billingAddress:address billingFirstName:@"Arthur" billingLastName:@"Dent" billingDOB:dob];
STPAPIClient *client = [[STPAPIClient alloc] initWithPublishableKey:apiKey];
XCTestExpectation *expectation = [self expectationWithDescription:@"Source creation"];
[client createSourceWithParams:params completion:^(STPSource *source, NSError * error) {
XCTAssertNil(error);
XCTAssertNotNil(source);
XCTAssertEqual(source.type, STPSourceTypeKlarna);
XCTAssertEqualObjects(source.amount, @(600));
XCTAssertEqualObjects(source.owner.address.line1, address.line1);
XCTAssertEqualObjects(source.klarnaDetails.purchaseCountry, @"UK");
XCTAssertEqual(source.redirect.status, STPSourceRedirectStatusPending);
XCTAssertEqualObjects(source.redirect.returnURL, [NSURL URLWithString:@"https://shop.example.com/return?redirect_merchant_name=xctest"]);
XCTAssertNotNil(source.redirect.url);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0f handler:nil];
}
- (void)testCreateSource_wechatPay {
STPSourceParams *params = [STPSourceParams wechatPayParamsWithAmount:1010
currency:@"usd"

View File

@ -74,6 +74,9 @@
sourceParams.rawTypeString = @"multibanco";
XCTAssertEqual(sourceParams.type, STPSourceTypeMultibanco);
sourceParams.rawTypeString = @"klarna";
XCTAssertEqual(sourceParams.type, STPSourceTypeKlarna);
sourceParams.rawTypeString = @"unknown";
XCTAssertEqual(sourceParams.type, STPSourceTypeUnknown);
@ -118,6 +121,9 @@
sourceParams.type = STPSourceTypeMultibanco;
XCTAssertEqualObjects(sourceParams.rawTypeString, @"multibanco");
sourceParams.type = STPSourceTypeKlarna;
XCTAssertEqualObjects(sourceParams.rawTypeString, @"klarna");
sourceParams.type = STPSourceTypeUnknown;
XCTAssertNil(sourceParams.rawTypeString);
}

View File

@ -133,6 +133,9 @@
case STPSourceTypeWeChatPay:
XCTAssertEqualObjects(string, @"wechat");
break;
case STPSourceTypeKlarna:
XCTAssertEqualObjects(string, @"klarna");
break;
case STPSourceTypeUnknown:
XCTAssertNil(string);
break;
@ -544,6 +547,8 @@
break;
case STPSourceTypeWeChatPay:
XCTAssertEqualObjects(source.label, @"WeChat Pay");
case STPSourceTypeKlarna:
XCTAssertEqualObjects(source.label, @"Klarna");
case STPSourceTypeUnknown:
XCTAssertEqualObjects(source.label, [STPCard stringFromBrand:STPCardBrandUnknown]);
break;

View File

@ -0,0 +1,98 @@
POST
/v1/sources$
200
application/json
Content-Type: application/json
Access-Control-Allow-Origin: *
Server: nginx
access-control-allow-methods: GET, POST, HEAD, OPTIONS, DELETE
access-control-expose-headers: Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required
access-control-max-age: 300
Cache-Control: no-cache, no-store
Date: Sat, 23 Nov 2019 00:01:21 GMT
stripe-version: 2019-05-16
access-control-allow-credentials: true
Content-Length: 3359
Connection: keep-alive
Strict-Transport-Security: max-age=31556926; includeSubDomains; preload
request-id: req_3QGqMegMD3Ec5K
{
"id" : "src_1Fhm3wBbvEcIpqUbSku1CReH",
"livemode" : false,
"amount" : 600,
"metadata" : {
},
"owner" : {
"address" : {
"state" : null,
"country" : "UK",
"line2" : null,
"city" : "London",
"line1" : "29 Arlington Avenue",
"postal_code" : "N1 7BE"
},
"phone" : "02012267709",
"verified_address" : null,
"verified_phone" : null,
"verified_email" : null,
"verified_name" : null,
"email" : "test@example.com",
"name" : null
},
"klarna" : {
"payment_method_categories" : "pay_over_time,pay_later",
"owner_dob_year" : "1952",
"client_token" : "eyJhbGciOiJSUzI1NiJ9.ewogICJzZXNzaW9uX2lkIiA6ICIzN2E4ZjY3Zi04ZWEzLTc4OTQtOTQ1ZS1mODFkYTE4OTE2ZGQiLAogICJiYXNlX3VybCIgOiAiaHR0cHM6Ly9rbGFybmEtcGF5bWVudHMtZXUucGxheWdyb3VuZC5rbGFybmEuY29tIiwKICAiZGVzaWduIiA6ICJrbGFybmEiLAogICJsYW5ndWFnZSIgOiAiZW4iLAogICJwdXJjaGFzZV9jb3VudHJ5IiA6ICJHQiIsCiAgInRyYWNlX2Zsb3ciIDogZmFsc2UsCiAgImVudmlyb25tZW50IiA6ICJwbGF5Z3JvdW5kIiwKICAibWVyY2hhbnRfbmFtZSIgOiAiSzUwMDczNSIsCiAgInNlc3Npb25fdHlwZSIgOiAiUEFZTUVOVFMiLAogICJjbGllbnRfZXZlbnRfYmFzZV91cmwiIDogImh0dHBzOi8vZXZ0LnBsYXlncm91bmQua2xhcm5hLmNvbSIsCiAgImV4cGVyaW1lbnRzIiA6IFsgXQp9.M6AMyAvuvv9QAfCMh5SZI5ib7FDc7Fno2FoUVRrbMvyTm69egbF1G4To258-U2mK8O2f-bHuXla9NjVmjr_p6NUKENWyXFJbv4Ix8ajdAZmBKZaVLnGM-Wd7TS0PqTUedONR74WxtUT3zbNncFJKBEekghfphZ8SPHv7GaXhFHK-Xr63mogrCnx7ZoBQlS9kPyJoe6IAqZdi84IF_R9ZSEY-58bJGh0VSTRfi_jxzh7Y2TR_n7tSaE5E9azg5KK-oRYnkP8VO1TA56zOzCz91QOLB29Xpqa-C0uk6X67AMt9ehuxGfNJn6u4aGwYHIZSRHmupprPRywbgZgfD2558Q",
"owner_dob_day" : "11",
"pay_later_asset_urls_standard" : "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_later_name" : "Pay later in 14 days",
"pay_over_time_asset_urls_standard" : "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"owner_dob_month" : "03",
"purchase_country" : "UK",
"pay_later_asset_urls_descriptive" : "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_later_redirect_url" : "https:\/\/payment-eu.playground.klarna.com\/7rT6I4M",
"first_name" : "Arthur",
"pay_over_time_asset_urls_descriptive" : "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"last_name" : "Dent",
"pay_over_time_name" : "Buy now, pay later",
"pay_over_time_redirect_url" : "https:\/\/payment-eu.playground.klarna.com\/7OcjKTB"
},
"source_order" : {
"amount" : 600,
"currency" : "gbp",
"email" : "test@example.com",
"items" : [
{
"amount" : 500,
"currency" : "gbp",
"quantity" : 2,
"description" : "Test Item",
"type" : "sku"
},
{
"amount" : 100,
"currency" : "gbp",
"quantity" : 1,
"description" : "Tax",
"type" : "tax"
}
]
},
"statement_descriptor" : null,
"redirect" : {
"status" : "pending",
"failure_reason" : null,
"url" : "https:\/\/hooks.stripe.com\/redirect\/authenticate\/src_1Fhm3wBbvEcIpqUbSku1CReH?client_secret=src_client_secret_GEEXx02X7WTKisrU5skuQhUo",
"return_url" : "https:\/\/shop.example.com\/return?redirect_merchant_name=xctest"
},
"type" : "klarna",
"object" : "source",
"usage" : "single_use",
"created" : 1574467280,
"client_secret" : "src_client_secret_GEEXx02X7WTKisrU5skuQhUo",
"flow" : "redirect",
"currency" : "gbp",
"status" : "pending"
}