Skip to content

Commit d4c9bce

Browse files
authored
Add FraudProtection Docs (#184)
* Update PayPalDataCollector to take a core config and remove PayPalDataCollectorEnvironment enum. * Add FraudProtection to list of cocoapods dependencies. * Add FraudProtection docs. * Update CHANGELOG. * Sort filenames in xcodeproj. * Fix lint errors. * Add canImport statements for CorePayments imports in the FraudProtection module. * Add @testable import for CorePayments in PayPalDataCollector test. * Add CorePayments.framework to link step for FraudProtection module in xcodeproj.
1 parent 7660350 commit d4c9bce

File tree

9 files changed

+113
-26
lines changed

9 files changed

+113
-26
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
* Add `CardVaultRequest` and `CardVaultResult` types for interacting with `vault` method
88
* Add `CardVaultDelegate` protocol to receive success and failure results
99
* Add `CardVaultDelegate` property to `CardClient`
10-
10+
* Breaking Changes
11+
* FraudProtection
12+
* Update `PayPalDataCollector` constructor to require a configuration instead of an environment
13+
* Remove `PayPalDataCollectorEnvironment` enum
14+
1115
## 0.0.10 (2023-08-14)
1216
* PayPalNativePayments
1317
* Bump `PayPalCheckout` to `1.0.0`

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ let package = Package(
6262
),
6363
.target(
6464
name: "FraudProtection",
65-
dependencies: ["PPRiskMagnes"]
65+
dependencies: ["CorePayments", "PPRiskMagnes"]
6666
),
6767
.binaryTarget(
6868
name: "PPRiskMagnes",

PayPal.podspec

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Pod::Spec.new do |s|
3737

3838
s.subspec "FraudProtection" do |s|
3939
s.source_files = "Sources/FraudProtection/*.swift"
40+
s.dependency "PayPal/CorePayments"
4041
s.vendored_frameworks = "Frameworks/XCFrameworks/PPRiskMagnes.xcframework"
4142
end
4243

PayPal.xcodeproj/project.pbxproj

+6-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
BC04837427B2FC7300FA7B46 /* URLSession+URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC04837327B2FC7300FA7B46 /* URLSession+URLSessionProtocol.swift */; };
6565
BC0A82A5270B9533006E9A21 /* ConfirmPaymentSourceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CE009F26F3DF100000CC46 /* ConfirmPaymentSourceRequest.swift */; };
6666
BC0A82A6270B9954006E9A21 /* ConfirmPaymentSourceRequest_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065A4DC226FCE1D20007014A /* ConfirmPaymentSourceRequest_Tests.swift */; };
67+
BC171FB12A8C156300B26DCB /* CoreConfig+MagnesSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC171FB02A8C156300B26DCB /* CoreConfig+MagnesSDK.swift */; };
68+
BC171FB22A8D6FE500B26DCB /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B9F85126B8750000D67843 /* CorePayments.framework */; };
6769
BC43406F27E91C2700F70193 /* PayPalCheckout in Frameworks */ = {isa = PBXBuildFile; productRef = BC43406E27E91C2700F70193 /* PayPalCheckout */; };
6870
BC7F8123275FC1350011EDC8 /* CardRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC7F8122275FC1350011EDC8 /* CardRequest.swift */; };
6971
BC900B7D27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC900B7C27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift */; };
@@ -123,7 +125,6 @@
123125
CB1A48052822BCED00BD8184 /* ImageAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A48042822BCED00BD8184 /* ImageAsset.swift */; };
124126
CB1AC3C22982CDB10081AED6 /* MockNativeCheckoutProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D25238B273979170099E4EB /* MockNativeCheckoutProvider.swift */; };
125127
CB22C018291049500097E592 /* PayPalPayLaterButton_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB22C017291049500097E592 /* PayPalPayLaterButton_Tests.swift */; };
126-
CB38546327F4B616003CE179 /* PayPalDataCollectorEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB38546227F4B616003CE179 /* PayPalDataCollectorEnvironment.swift */; };
127128
CB4BE27D2847AF6F00EA2DD1 /* SCA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE27C2847AF6F00EA2DD1 /* SCA.swift */; };
128129
CB4BE27E2847EA7D00EA2DD1 /* WebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784827EB629100FF4C0E /* WebAuthenticationSession.swift */; };
129130
CB4BE2802847F01000EA2DD1 /* CardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */; };
@@ -249,6 +250,7 @@
249250
80FC261C29847AC7008EC841 /* HTTP_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTP_Tests.swift; sourceTree = "<group>"; };
250251
BC04836E27B2FB3600FA7B46 /* URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = "<group>"; };
251252
BC04837327B2FC7300FA7B46 /* URLSession+URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+URLSessionProtocol.swift"; sourceTree = "<group>"; };
253+
BC171FB02A8C156300B26DCB /* CoreConfig+MagnesSDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreConfig+MagnesSDK.swift"; sourceTree = "<group>"; };
252254
BC7F8122275FC1350011EDC8 /* CardRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardRequest.swift; sourceTree = "<group>"; };
253255
BC900B7C27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeCheckoutDelegate.swift; sourceTree = "<group>"; };
254256
BC9C18D327D2775A0019B541 /* MockDeviceInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDeviceInspector.swift; sourceTree = "<group>"; };
@@ -310,7 +312,6 @@
310312
CB1A47FD2820C10700BD8184 /* PaymentButtonFundingSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButtonFundingSource.swift; sourceTree = "<group>"; };
311313
CB1A48042822BCED00BD8184 /* ImageAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageAsset.swift; sourceTree = "<group>"; };
312314
CB22C017291049500097E592 /* PayPalPayLaterButton_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalPayLaterButton_Tests.swift; sourceTree = "<group>"; };
313-
CB38546227F4B616003CE179 /* PayPalDataCollectorEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalDataCollectorEnvironment.swift; sourceTree = "<group>"; };
314315
CB4BE27C2847AF6F00EA2DD1 /* SCA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SCA.swift; sourceTree = "<group>"; };
315316
CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardDelegate.swift; sourceTree = "<group>"; };
316317
CB4BE285284802C200EA2DD1 /* PaymentSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSource.swift; sourceTree = "<group>"; };
@@ -404,6 +405,7 @@
404405
isa = PBXFrameworksBuildPhase;
405406
buildActionMask = 2147483647;
406407
files = (
408+
BC171FB22A8D6FE500B26DCB /* CorePayments.framework in Frameworks */,
407409
BCF735E327D158B400A52E03 /* PPRiskMagnes.xcframework in Frameworks */,
408410
);
409411
runOnlyForDeploymentPostprocessing = 0;
@@ -612,13 +614,13 @@
612614
BCF735C127D157CD00A52E03 /* FraudProtection */ = {
613615
isa = PBXGroup;
614616
children = (
617+
BC171FB02A8C156300B26DCB /* CoreConfig+MagnesSDK.swift */,
615618
BCF735EC27D1650800A52E03 /* DeviceInspector.swift */,
616619
BCF735EE27D1652100A52E03 /* DeviceInspectorProtocol.swift */,
617620
BCF735E827D15D2600A52E03 /* MagnesSDKProtocol.swift */,
618621
BC9C18D727D279C70019B541 /* MagnesSDKResult.swift */,
619622
BCF735EA27D160E800A52E03 /* MagnesSetupParams.swift */,
620623
BCF735E627D15AB800A52E03 /* PayPalDataCollector.swift */,
621-
CB38546227F4B616003CE179 /* PayPalDataCollectorEnvironment.swift */,
622624
);
623625
name = FraudProtection;
624626
path = Sources/FraudProtection;
@@ -1388,7 +1390,7 @@
13881390
buildActionMask = 2147483647;
13891391
files = (
13901392
BCF735EF27D1652100A52E03 /* DeviceInspectorProtocol.swift in Sources */,
1391-
CB38546327F4B616003CE179 /* PayPalDataCollectorEnvironment.swift in Sources */,
1393+
BC171FB12A8C156300B26DCB /* CoreConfig+MagnesSDK.swift in Sources */,
13921394
BCF735E727D15AB800A52E03 /* PayPalDataCollector.swift in Sources */,
13931395
BCF735EB27D160E800A52E03 /* MagnesSetupParams.swift in Sources */,
13941396
BCF735E927D15D2600A52E03 /* MagnesSDKProtocol.swift in Sources */,

Sources/FraudProtection/PayPalDataCollectorEnvironment.swift Sources/FraudProtection/CoreConfig+MagnesSDK.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import PPRiskMagnes
2+
#if canImport(CorePayments)
3+
import CorePayments
4+
#endif
25

3-
/// Enum of environments to use with PayPalDataCollector
4-
public enum PayPalDataCollectorEnvironment {
5-
case sandbox
6-
case live
7-
6+
extension CoreConfig {
7+
88
var magnesEnvironment: MagnesSDK.Environment {
9-
switch self {
9+
switch environment {
1010
case .sandbox:
1111
return .SANDBOX
1212
case .live:

Sources/FraudProtection/PayPalDataCollector.swift

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import Foundation
22
import PPRiskMagnes
3+
#if canImport(CorePayments)
4+
import CorePayments
5+
#endif
36

47
/// Enables you to collect data about a customer's device and correlate it with a session identifier on your server.
58
public class PayPalDataCollector {
6-
9+
710
// MARK: - Properties
811

912
private let magnesSDK: MagnesSDKProtocol
@@ -14,13 +17,13 @@ public class PayPalDataCollector {
1417

1518
/// Construct an instance to collect device data to send to your server.
1619
/// - Parameter environment: enviroment for the data collector
17-
public convenience init(environment: PayPalDataCollectorEnvironment) {
18-
self.init(environment: environment, magnesSDK: MagnesSDK.shared(), deviceInspector: DeviceInspector())
20+
public convenience init(config: CoreConfig) {
21+
self.init(config: config, magnesSDK: MagnesSDK.shared(), deviceInspector: DeviceInspector())
1922
}
2023

2124
/// internal constructor for testing
22-
init(environment: PayPalDataCollectorEnvironment, magnesSDK: MagnesSDKProtocol, deviceInspector: DeviceInspectorProtocol) {
23-
self.magnesEnvironment = environment.magnesEnvironment
25+
init(config: CoreConfig, magnesSDK: MagnesSDKProtocol, deviceInspector: DeviceInspectorProtocol) {
26+
self.magnesEnvironment = config.magnesEnvironment
2427
self.magnesSDK = magnesSDK
2528
self.deviceInspector = deviceInspector
2629
}

UnitTests/FraudProtectionTests/DeviceInspector_Tests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class DeviceInspector_Tests: XCTestCase {
2020
#else
2121
let sut = DeviceInspector()
2222
let newIdentifier = UUID()
23-
let result = sut.paypalDeviceIdentifier(newIdentifier: newIdentifier)
23+
let result = sut.payPalDeviceIdentifier(newIdentifier: newIdentifier)
2424

2525
XCTAssertNotEqual(newIdentifier.uuidString, result)
2626
#endif

UnitTests/FraudProtectionTests/PayPalDataCollector_Tests.swift

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import XCTest
2+
@testable import CorePayments
23
@testable import FraudProtection
34

45
class PayPalDataCollector_Tests: XCTestCase {
56

67
private var deviceInspector = MockDeviceInspector()
78
private var magnesSDK = MockMagnesSDK()
8-
9+
10+
let sandboxConfig = CoreConfig(clientID: "mockClientID", environment: .sandbox)
11+
let liveConfig = CoreConfig(clientID: "mockClientID", environment: .live)
12+
913
func testCollectDeviceData_setsMagnesEnvironmentToSANDBOX() {
10-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
14+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
1115
_ = sut.collectDeviceData()
1216

1317
XCTAssertEqual(.SANDBOX, magnesSDK.capturedSetupParams?.env)
1418
}
1519

1620
func testCollectDeviceData_setsMagnesEnvironmentToLIVE() {
17-
let sut = PayPalDataCollector(environment: .live, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
21+
let sut = PayPalDataCollector(config: liveConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
1822
_ = sut.collectDeviceData()
1923

2024
XCTAssertEqual(.LIVE, magnesSDK.capturedSetupParams?.env)
@@ -23,28 +27,28 @@ class PayPalDataCollector_Tests: XCTestCase {
2327
func testCollectDeviceData_setsMagnesAppGUIDToTheCurrentDeviceID() {
2428
deviceInspector.stubPayPalDeviceIdentifierWithValue("sample_device_identifier")
2529

26-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
30+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
2731
_ = sut.collectDeviceData()
2832

2933
XCTAssertEqual("sample_device_identifier", magnesSDK.capturedSetupParams?.appGuid)
3034
}
3135

3236
func testCollectDeviceData_disablesMagnesRemoteConfiguration() {
33-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
37+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
3438
_ = sut.collectDeviceData()
3539

3640
XCTAssertEqual(false, magnesSDK.capturedSetupParams?.isRemoteConfigDisabled)
3741
}
3842

3943
func testCollectDeviceData_disablesMagnesBeacon() {
40-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
44+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
4145
_ = sut.collectDeviceData()
4246

4347
XCTAssertEqual(false, magnesSDK.capturedSetupParams?.isBeaconDisabled)
4448
}
4549

4650
func testCollectDeviceData_setsMagnesSourceToPAYPAL() {
47-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
51+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
4852
_ = sut.collectDeviceData()
4953

5054
XCTAssertEqual(.PAYPAL, magnesSDK.capturedSetupParams?.source)
@@ -55,7 +59,7 @@ class PayPalDataCollector_Tests: XCTestCase {
5559
let magnesResult = MockMagnesSDKResult(payPalClientMetaDataId: "new_client_metadata_id")
5660
magnesSDK.stubCollectDeviceData(forArgs: args, withValue: magnesResult)
5761

58-
let sut = PayPalDataCollector(environment: .sandbox, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
62+
let sut = PayPalDataCollector(config: sandboxConfig, magnesSDK: magnesSDK, deviceInspector: deviceInspector)
5963
let result = sut.collectDeviceData(additionalData: ["sample": "data"])
6064

6165
let expected = """

docs/FraudProtection/README.md

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Fraud Protection
2+
3+
The FraudProtection module in the PayPal SDK enables merchants to collect user device data and associate it with payment transactions to reduce the risk of processing a fraudulent transaction.
4+
5+
Follow these steps to add fraud protection:
6+
7+
1. [Setup a PayPal Developer Account](#setup-a-paypal-developer-account)
8+
2. [Add FraudProtection Module](#add-fraudprotection-module)
9+
3. [Go Live](#go-live)
10+
11+
## Setup a PayPal Developer Account
12+
13+
You will need to set up authorization to use the PayPal Payments SDK.
14+
Follow the steps in [Get Started](https://developer.paypal.com/api/rest/#link-getstarted) to create a client ID.
15+
16+
You will need a server integration to create an order and capture funds using [PayPal Orders v2 API](https://developer.paypal.com/docs/api/orders/v2).
17+
For initial setup, the `curl` commands below can be used in place of a server SDK.
18+
19+
## Add FraudProtection Module
20+
21+
### 1. Add the Payments SDK FraudProtection module to your app
22+
23+
#### Swift Package Manager
24+
25+
In Xcode, add the PayPal SDK as a [package dependency](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) to your Xcode project. Enter https://github.com/paypal/paypal-ios as the package URL. Tick the `FraudProtection` checkbox to add the Fraud Protection library to your app.
26+
27+
#### CocoaPods
28+
29+
Include the `FraudProtection` sub-module in your `Podfile`:
30+
31+
```ruby
32+
pod 'PayPal/FraudProtection'
33+
```
34+
35+
### 2. Initiate the Payments SDK
36+
37+
Create a `CoreConfig` using a [client id](https://developer.paypal.com/api/rest/):
38+
39+
```swift
40+
let config = CoreConfig(clientID:"<CLIENT_ID>", environment: .sandbox)
41+
```
42+
43+
Create a `PayPalDataCollector` to retrieve a PayPal Client Metadata ID (CMID). You can use a CMID when calling Authorize or Capture to add fraud protection to your transactions.
44+
45+
```swift
46+
let payPalDataCollector = PayPalDataCollector(config: config)
47+
let cmid = payPalDataCollector.collectDeviceData()
48+
```
49+
50+
### 3. Send PayPal-Client-Metadata-Id Header on Capture / Authorize
51+
52+
To enable fraud protection, add a `PayPal-Client-Metadata-Id` HTTP header to your call to Capture (or Authorize), and set its value to the `cmid` value obtained in the previous step:
53+
54+
```bash
55+
# for capture
56+
curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/<ORDER_ID>/capture' \
57+
--header 'Content-Type: application/json' \
58+
--header 'Authorization: Bearer <ACCESS_TOKEN>' \
59+
--header 'PayPal-Client-Metadata-Id: <CMID>' \
60+
--data-raw ''
61+
62+
# for authorize
63+
curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/<ORDER_ID>/authorize' \
64+
--header 'Content-Type: application/json' \
65+
--header 'Authorization: Bearer <ACCESS_TOKEN>' \
66+
--header 'PayPal-Client-Metadata-Id: <CMID>' \
67+
--data-raw ''
68+
```
69+
70+
## Go Live
71+
72+
Follow [these instructions](https://developer.paypal.com/api/rest/production/) to prepare your integration to go live.
73+

0 commit comments

Comments
 (0)