Skip to content

Commit

Permalink
Merge branch 'main' into button-color-update
Browse files Browse the repository at this point in the history
  • Loading branch information
stechiu authored Jan 3, 2024
2 parents 6849ec8 + e86898f commit 98e3a31
Show file tree
Hide file tree
Showing 37 changed files with 839 additions and 315 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ jobs:
- name: Update version
run: |
today=$(date +'%Y-%m-%d')
sed -i '' 's/## unreleased.*/## '"${{ github.event.inputs.version }}"' ('"$today"')/' CHANGELOG.md
sed -i '' 's/\(s\.version *= *\).*/\1"'"${{ github.event.inputs.version }}"'\"/' PayPal.podspec
sed -i '' 's/payPalSDKVersion: String =.*/payPalSDKVersion: String = "${{ github.event.inputs.version }}"/' Sources/CorePayments/PayPalCoreConstants.swift
plutil -replace CFBundleShortVersionString -string ${{ github.event.inputs.version }} -- 'Demo/Demo/Info.plist'
git add .
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
* PaymentButtons
* Remove deprecated colors `black`, `silver`, `blue`, and `darkBlue`

* PayPalWebPayments
* Add `vault(url:)` method to `PayPalWebCheckoutClient`
* Add `PayPalVaultResult` type to return vault result
* Add `PayPalVaultDelegate` to handle results from vault flow
* Add `PayPalWebCheckoutClientError.paypalVaultResponseError` for missing or invalid response from vaulting

## 1.1.0 (2023-11-16)
* PayPalNativePayments
* Bump `PayPalCheckout` to `1.2.0`
Expand Down
60 changes: 44 additions & 16 deletions Demo/Demo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions Demo/Demo/Extensions/UIViewController+Extension.swift

This file was deleted.

12 changes: 9 additions & 3 deletions Demo/Demo/Models/PaymentTokenResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ struct Customer: Codable, Equatable {

struct PaymentSource: Decodable, Equatable {

let card: BasicCard
var card: CardPaymentSource?
var paypal: PayPalPaymentSource?
}

struct BasicCard: Decodable, Equatable {
struct CardPaymentSource: Decodable, Equatable {

let brand: String?
let lastDigits: String
let expiry: String
}

struct PayPalPaymentSource: Decodable, Equatable {

let emailAddress: String
}
82 changes: 68 additions & 14 deletions Demo/Demo/Models/SetupTokenRequest.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,71 @@
import Foundation

struct SetUpTokenRequest {

struct VaultExperienceContext: Encodable {

let returnUrl = "sdk.ios.paypal://vault/success"
let cancelUrl = "sdk.ios.paypal://vault/cancel"

enum CodingKeys: String, CodingKey {
case returnUrl = "return_url"
case cancelUrl = "cancel_url"
}
}

struct PayPal: Encodable {

var usageType: String
let experienceContext = VaultExperienceContext()

enum CodingKeys: String, CodingKey {
case usageType = "usage_type"
case experienceContext = "experience_context"
}
}

enum PaymentSourceType: Encodable {
case card
case paypal(usageType: String)

private enum CodingKeys: String, CodingKey {
case card, paypal
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .card:
try container.encode(EmptyBodyParams(), forKey: .card)
case .paypal(let usageType):
try container.encode(PayPal(usageType: usageType), forKey: .paypal)
}
}
}

struct VaultCustomer: Encodable {

var id: String?

private enum CodingKeys: String, CodingKey {
case id
}
}

struct SetupTokenRequestBody: Encodable {

var customer: VaultCustomer?
let paymentSource: PaymentSourceType

enum CodingKeys: String, CodingKey {
case paymentSource = "payment_source"
case customer
}
}

struct SetUpTokenRequest: Encodable {

let customerID: String?

let paymentSource: PaymentSourceType

var path: String {
"/setup_tokens/"
}
Expand All @@ -15,17 +77,9 @@ struct SetUpTokenRequest {
var headers: [String: String] {
["Content-Type": "application/json"]
}

var body: Data? {
let requestBody: [String: Any] = [
"customer": [
"id": customerID
],
"payment_source": [
"card": [:]
]
]

return try? JSONSerialization.data(withJSONObject: requestBody)
let requestBodyParam = SetupTokenRequestBody(customer: VaultCustomer(id: customerID), paymentSource: paymentSource)
return try? JSONEncoder().encode(requestBodyParam)
}
}
9 changes: 9 additions & 0 deletions Demo/Demo/Models/SetupTokenResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ struct SetUpTokenResponse: Decodable, Equatable {

let id, status: String
let customer: Customer?
let links: [Link]
var paypalURL: String? {
links.first { $0.rel == "approve" }?.href
}

struct Customer: Decodable {

let id: String
}

struct Link: Decodable {

let href, rel, method: String
}
}
10 changes: 7 additions & 3 deletions Demo/Demo/Networking/DemoMerchantAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ final class DemoMerchantAPI {
private init() {}

// MARK: Public Methods

func getSetupToken(customerID: String? = nil, selectedMerchantIntegration: MerchantIntegration) async throws -> SetUpTokenResponse {

func getSetupToken(
customerID: String? = nil,
selectedMerchantIntegration: MerchantIntegration,
paymentSourceType: PaymentSourceType
) async throws -> SetUpTokenResponse {
do {
// TODO: pass in headers depending on integration type
// Different request struct or integration type property
// in SetUpTokenRequest to conditionally add header
let request = SetUpTokenRequest(customerID: customerID)
let request = SetUpTokenRequest(customerID: customerID, paymentSource: paymentSourceType)
let urlRequest = try createSetupTokenUrlRequest(
setupTokenRequest: request,
environment: DemoSettings.environment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@ struct CardVaultView: View {
VStack(spacing: 16) {
CreateSetupTokenView(
selectedMerchantIntegration: DemoSettings.merchantIntegration,
cardVaultViewModel: cardVaultViewModel
vaultViewModel: cardVaultViewModel,
paymentSourceType: PaymentSourceType.card
)
SetupTokenResultView(cardVaultViewModel: cardVaultViewModel)
SetupTokenResultView(vaultViewModel: cardVaultViewModel)
if let setupToken = cardVaultViewModel.state.setupToken {
UpdateSetupTokenView(cardVaultViewModel: cardVaultViewModel, setupToken: setupToken.id)
}
UpdateSetupTokenResultView(cardVaultViewModel: cardVaultViewModel)
if let updateSetupToken = cardVaultViewModel.state.updateSetupToken {
CreatePaymentTokenView(
cardVaultViewModel: cardVaultViewModel,
vaultViewModel: cardVaultViewModel,
selectedMerchantIntegration: DemoSettings.merchantIntegration,
setupToken: updateSetupToken.id
)
}
PaymentTokenResultView(cardVaultViewModel: cardVaultViewModel)
PaymentTokenResultView(vaultViewModel: cardVaultViewModel)
switch cardVaultViewModel.state.paymentTokenResponse {
case .loaded, .error:
VStack {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct UpdateSetupTokenResultView: View {
}
}

func getSuccessView(updateSetupTokenResponse: CardVaultState.UpdateSetupTokenResult) -> some View {
func getSuccessView(updateSetupTokenResponse: UpdateSetupTokenResult) -> some View {
VStack(spacing: 16) {
HStack {
Text("Vault Success")
Expand Down
19 changes: 19 additions & 0 deletions Demo/Demo/SwiftUIComponents/CommonComponents/LabelViewText.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import SwiftUI

struct LabelViewText: View {

var titleText: String
var bodyText: String

var body: some View {
HStack {
Text(titleText).fontWeight(.bold)
Text(bodyText)
}
}

init(_ titleText: String, bodyText: String) {
self.titleText = titleText
self.bodyText = bodyText
}
}
6 changes: 6 additions & 0 deletions Demo/Demo/SwiftUIComponents/FeatureSelectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ struct FeatureSelectionView: View {
} label: {
Text("PayPal Web")
}
NavigationLink {
PayPalVaultView()
.navigationTitle("PayPal Vaulting")
} label: {
Text("PayPal Vaulting")
}
NavigationLink {
SwiftUINativeCheckoutDemo()
} label: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI
import PayPalWebPayments

struct PayPalVaultResultView: View {

@ObservedObject var viewModel: PayPalVaultViewModel

var body: some View {
switch viewModel.state.paypalVaultTokenResponse {
case .idle, .loading:
EmptyView()
case .loaded(let vaultResult):
getSuccessView(result: vaultResult)
case .error(let errorMessage):
ErrorView(errorMessage: errorMessage)
}
}

func getSuccessView(result: PayPalVaultResult) -> some View {
VStack(spacing: 16) {
HStack {
Text("Vault Success")
.font(.system(size: 20))
Spacer()
}
LeadingText("ID", weight: .bold)
LeadingText("\(result.tokenID)")
LeadingText("Status", weight: .bold)
LeadingText("APPROVED")
LeadingText("Approval Session ID", weight: .bold)
LeadingText("\(result.approvalSessionID)")
}
.frame(maxWidth: .infinity)
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2)
.padding(5)
)
}
}
64 changes: 64 additions & 0 deletions Demo/Demo/SwiftUIComponents/PayPalVaultViews/PayPalVaultView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import SwiftUI

struct PayPalVaultView: View {

@StateObject var paypalVaultViewModel = PayPalVaultViewModel()

var body: some View {
ScrollView {
ScrollViewReader { scrollView in
VStack(spacing: 16) {
CreateSetupTokenView(
selectedMerchantIntegration: DemoSettings.merchantIntegration,
vaultViewModel: paypalVaultViewModel,
paymentSourceType: PaymentSourceType.paypal(usageType: "MERCHANT")
)
SetupTokenResultView(vaultViewModel: paypalVaultViewModel)
if let url = paypalVaultViewModel.state.setupToken?.paypalURL {
Button("Vault PayPal") {
Task {
await paypalVaultViewModel.vault(url: url)
}
}
.buttonStyle(RoundedBlueButtonStyle())
.padding()
}
PayPalVaultResultView(viewModel: paypalVaultViewModel)
if let paypalVaultResult = paypalVaultViewModel.state.paypalVaultToken {
CreatePaymentTokenView(
vaultViewModel: paypalVaultViewModel,
selectedMerchantIntegration: DemoSettings.merchantIntegration,
setupToken: paypalVaultResult.tokenID
)
}
PaymentTokenResultView(vaultViewModel: paypalVaultViewModel)
switch paypalVaultViewModel.state.paymentTokenResponse {
case .loaded, .error:
VStack {
Button("Reset") {
paypalVaultViewModel.resetState()
}
.foregroundColor(.black)
.padding()
.frame(maxWidth: .infinity)
.background(.gray)
.cornerRadius(10)
}
.padding(5)
default:
EmptyView()
}
Text("")
.id("bottomView")
.frame(maxWidth: .infinity, alignment: .top)
.padding(.horizontal, 10)
.onChange(of: paypalVaultViewModel.state) { _ in
withAnimation {
scrollView.scrollTo("bottomView")
}
}
}
}
}
}
}
Loading

0 comments on commit 98e3a31

Please sign in to comment.