Skip to content

Commit 759db93

Browse files
committed
5976 Add WCPayChargeMapper to decode WCPayCharge
1 parent 9548f08 commit 759db93

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@
8686
0359EA1727AAC7740048DE2D /* WCPayCardFunding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359EA1627AAC7740048DE2D /* WCPayCardFunding.swift */; };
8787
0359EA1927AAC79E0048DE2D /* WCPayCardPresentPaymentDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359EA1827AAC79E0048DE2D /* WCPayCardPresentPaymentDetails.swift */; };
8888
0359EA1B27AAC7CC0048DE2D /* WCPayCardPresentReceiptDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359EA1A27AAC7CC0048DE2D /* WCPayCardPresentReceiptDetails.swift */; };
89+
0359EA1D27AADE000048DE2D /* WCPayChargeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */; };
90+
0359EA1F27AAE4680048DE2D /* WCPayChargeMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0359EA1E27AAE4680048DE2D /* WCPayChargeMapperTests.swift */; };
91+
0359EA2127AAE58C0048DE2D /* wcpay-charge.json in Resources */ = {isa = PBXBuildFile; fileRef = 0359EA2027AAE58C0048DE2D /* wcpay-charge.json */; };
8992
03DCB72626244B9B00C8953D /* Coupon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DCB72526244B9B00C8953D /* Coupon.swift */; };
9093
03DCB7402624AD7D00C8953D /* CouponListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DCB73F2624AD7D00C8953D /* CouponListMapper.swift */; };
9194
03DCB7442624AD9B00C8953D /* CouponListMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DCB7432624AD9A00C8953D /* CouponListMapperTests.swift */; };
@@ -749,6 +752,9 @@
749752
0359EA1627AAC7740048DE2D /* WCPayCardFunding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCPayCardFunding.swift; sourceTree = "<group>"; };
750753
0359EA1827AAC79E0048DE2D /* WCPayCardPresentPaymentDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCPayCardPresentPaymentDetails.swift; sourceTree = "<group>"; };
751754
0359EA1A27AAC7CC0048DE2D /* WCPayCardPresentReceiptDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCPayCardPresentReceiptDetails.swift; sourceTree = "<group>"; };
755+
0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCPayChargeMapper.swift; sourceTree = "<group>"; };
756+
0359EA1E27AAE4680048DE2D /* WCPayChargeMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCPayChargeMapperTests.swift; sourceTree = "<group>"; };
757+
0359EA2027AAE58C0048DE2D /* wcpay-charge.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-charge.json"; sourceTree = "<group>"; };
752758
03DCB72526244B9B00C8953D /* Coupon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coupon.swift; sourceTree = "<group>"; };
753759
03DCB73F2624AD7D00C8953D /* CouponListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponListMapper.swift; sourceTree = "<group>"; };
754760
03DCB7432624AD9A00C8953D /* CouponListMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponListMapperTests.swift; sourceTree = "<group>"; };
@@ -1995,6 +2001,7 @@
19952001
02A26F1A2744F5FC008E4EDB /* wp-site-settings.json */,
19962002
DEC51AEC2768A0AD009F3DF4 /* systemStatusWithPluginsOnly.json */,
19972003
077F39D726A58EB600ABEADC /* systemStatus.json */,
2004+
0359EA2027AAE58C0048DE2D /* wcpay-charge.json */,
19982005
45CCFCE927A2E59B0012E8CB /* inbox-note-list.json */,
19992006
);
20002007
path = Responses;
@@ -2082,6 +2089,7 @@
20822089
02C11275274285FF00F4F0B4 /* WooCommerceAvailabilityMapper.swift */,
20832090
02BE0A7A274B695F001176D2 /* WordPressMediaMapper.swift */,
20842091
02C112772742862600F4F0B4 /* WordPressSiteSettingsMapper.swift */,
2092+
0359EA1C27AADE000048DE2D /* WCPayChargeMapper.swift */,
20852093
);
20862094
path = Mapper;
20872095
sourceTree = "<group>";
@@ -2198,6 +2206,7 @@
21982206
CC0786C8267BB32800BA9AC1 /* ShippingLabelStatusMapperTests.swift */,
21992207
02C254D22563992900A04423 /* OrderShippingLabelListMapperTests.swift */,
22002208
FE28F6E926842E49004465C7 /* UserMapperTests.swift */,
2209+
0359EA1E27AAE4680048DE2D /* WCPayChargeMapperTests.swift */,
22012210
);
22022211
path = Mapper;
22032212
sourceTree = "<group>";
@@ -2580,6 +2589,7 @@
25802589
D800DA0E25EFEC21001E13CE /* wcpay-connection-token.json in Resources */,
25812590
74159628224D63CE003C21CF /* settings-product-alt.json in Resources */,
25822591
45AB8B2024AB3E1F00B5B36E /* product-tags-empty.json in Resources */,
2592+
0359EA2127AAE58C0048DE2D /* wcpay-charge.json in Resources */,
25832593
451274A625276C82009911FF /* product-variation.json in Resources */,
25842594
020D07C223D858BB00FD9580 /* media-upload.json in Resources */,
25852595
31A451D127863A2E00FE81AA /* stripe-account-rejected-other.json in Resources */,
@@ -2862,6 +2872,7 @@
28622872
D8FBFF0D22D3AF4A006E3336 /* StatsGranularityV4.swift in Sources */,
28632873
261870782540A252006522A1 /* ShippingLineTax.swift in Sources */,
28642874
74046E1B217A684D007DD7BF /* SiteSettingsRemote.swift in Sources */,
2875+
0359EA1D27AADE000048DE2D /* WCPayChargeMapper.swift in Sources */,
28652876
B5C6FCCF20A3592900A4F8E4 /* OrderItem.swift in Sources */,
28662877
02C254A025636F6900A04423 /* ShippingLabelRefund.swift in Sources */,
28672878
26B2F74124C1F2C10065CCC8 /* LeaderboardsRemote.swift in Sources */,
@@ -2995,6 +3006,7 @@
29953006
74C8F06A20EEBC8C00B6EDC9 /* OrderMapperTests.swift in Sources */,
29963007
45152835257A8F490076B03C /* ProductAttributeListMapperTests.swift in Sources */,
29973008
077F39DA26A58ED700ABEADC /* SystemStatusRemoteTests.swift in Sources */,
3009+
0359EA1F27AAE4680048DE2D /* WCPayChargeMapperTests.swift in Sources */,
29983010
31D27C952602B737002EDB1D /* SitePluginsRemoteTests.swift in Sources */,
29993011
DEC51AFB2769C66B009F3DF4 /* SystemStatusMapperTests.swift in Sources */,
30003012
74AB5B4D21AF354E00859C12 /* SiteAPIMapperTests.swift in Sources */,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Foundation
2+
3+
/// Mapper: WCPayCharge
4+
///
5+
struct WCPayChargeMapper: Mapper {
6+
let siteID: Int64
7+
8+
/// (Attempts) to convert a dictionary into an account.
9+
///
10+
func map(response: Data) throws -> WCPayCharge {
11+
let decoder = JSONDecoder()
12+
decoder.userInfo = [.siteID: siteID]
13+
14+
/// Needed for currentDeadline, which is given as a UNIX timestamp.
15+
/// Unfortunately other properties use other formats for dates, but we
16+
/// can cross that bridge when we need those decoded.
17+
decoder.dateDecodingStrategy = .secondsSince1970
18+
19+
return try decoder.decode(WCPayChargeEnvelope.self, from: response).charge
20+
}
21+
}
22+
23+
/// WCPayChargeEnvelope Disposable Entity
24+
///
25+
/// Account endpoint returns the requested account in the `data` key. This entity
26+
/// allows us to parse it with JSONDecoder.
27+
///
28+
private struct WCPayChargeEnvelope: Decodable {
29+
let charge: WCPayCharge
30+
31+
private enum CodingKeys: String, CodingKey {
32+
case charge = "data"
33+
}
34+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import XCTest
2+
@testable import Networking
3+
4+
class WCPayChargeMapperTests: XCTestCase {
5+
/// Dummy Site ID.
6+
///
7+
fileprivate let dummySiteID: Int64 = 12983476
8+
9+
/// Verifies that the WCPayCharge is parsed.
10+
///
11+
func test_WCPayCharge_map_parses_all_coupons_in_response() throws {
12+
let wcpayCharge = try mapRetrieveWCPayChargeResponse()
13+
XCTAssertNotNil(wcpayCharge)
14+
}
15+
16+
/// Verifies that the `siteID` is added in the mapper, because it's not provided by the API endpoint
17+
///
18+
func test_WCPayCharge_map_includes_siteID_in_parsed_results() throws {
19+
let wcpayCharge = try mapRetrieveWCPayChargeResponse()
20+
XCTAssertEqual(wcpayCharge.siteID, dummySiteID)
21+
}
22+
23+
/// Verifies that the fields are all parsed correctly
24+
///
25+
func test_WCPayCharge_map_parses_all_fields_in_result() throws {
26+
let wcpayCharge = try mapRetrieveWCPayChargeResponse()
27+
28+
let expectedCreatedDate = Date.init(timeIntervalSince1970: 1643280767) //2022-01-27 10:52:47 UTC
29+
30+
let expectedPaymentMethodDetails = WCPayPaymentMethodDetails.cardPresent(
31+
details: .init(brand: .visa,
32+
last4: "9969",
33+
funding: .credit,
34+
receipt: .init(accountType: .credit,
35+
applicationPreferredName: "Stripe Credit",
36+
dedicatedFileName: "A000000003101001")))
37+
38+
let expectedWcpayCharge = WCPayCharge(siteID: dummySiteID,
39+
id: "ch_3KMVap2EdyGr1FMV1uKJEWtg",
40+
amount: 1800,
41+
amountCaptured: 1800,
42+
amountRefunded: 0,
43+
authorizationCode: "123456",
44+
captured: true,
45+
created: expectedCreatedDate,
46+
currency: "usd",
47+
paid: true,
48+
paymentIntentID: "pi_3KMVap2EdyGr1FMV16atNgK9",
49+
paymentMethodID: "pm_1KMVas2EdyGr1FMVnleuPovE",
50+
paymentMethodDetails: expectedPaymentMethodDetails,
51+
refunded: false,
52+
status: .succeeded)
53+
XCTAssertEqual(wcpayCharge, expectedWcpayCharge)
54+
}
55+
}
56+
57+
58+
// MARK: - Test Helpers
59+
///
60+
private extension WCPayChargeMapperTests {
61+
62+
/// Returns the CouponMapper output upon receiving `filename` (Data Encoded)
63+
///
64+
func mapWCPayCharge(from filename: String) throws -> WCPayCharge {
65+
guard let response = Loader.contentsOf(filename) else {
66+
throw FileNotFoundError()
67+
}
68+
69+
return try WCPayChargeMapper(siteID: dummySiteID).map(response: response)
70+
}
71+
72+
/// Returns the CouponMapper output from `coupon.json`
73+
///
74+
func mapRetrieveWCPayChargeResponse() throws -> WCPayCharge {
75+
return try mapWCPayCharge(from: "wcpay-charge")
76+
}
77+
78+
struct FileNotFoundError: Error {}
79+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
{
2+
"data": {
3+
"id": "ch_3KMVap2EdyGr1FMV1uKJEWtg",
4+
"object": "charge",
5+
"amount": 1800,
6+
"amount_captured": 1800,
7+
"amount_refunded": 0,
8+
"application": "ca_Ex84e31yMTLaNU5ozQvi5woLclpIDVpX",
9+
"application_fee": "fee_1KMVaw2EdyGr1FMVkom8DN98",
10+
"application_fee_amount": 57,
11+
"authorization_code": "123456",
12+
"balance_transaction": {
13+
"id": "txn_3KMVap2EdyGr1FMV1M2Xw1C4",
14+
"object": "balance_transaction",
15+
"amount": 1800,
16+
"available_on": 1643846400,
17+
"created": 1643280770,
18+
"currency": "usd",
19+
"description": "In-Person Payment for Order #205 for My WordPress Site",
20+
"exchange_rate": null,
21+
"fee": 57,
22+
"fee_details": [
23+
{
24+
"amount": 57,
25+
"application": "ca_Ex84e31yMTLaNU5ozQvi5woLclpIDVpX",
26+
"currency": "usd",
27+
"description": "WooCommerce Payments application fee",
28+
"type": "application_fee"
29+
}
30+
],
31+
"net": 1743,
32+
"reporting_category": "charge",
33+
"source": "ch_3KMVap2EdyGr1FMV1uKJEWtg",
34+
"status": "pending",
35+
"type": "charge"
36+
},
37+
"billing_details": {
38+
"address": {
39+
"city": null,
40+
"country": null,
41+
"line1": null,
42+
"line2": null,
43+
"postal_code": null,
44+
"state": null
45+
},
46+
"email": null,
47+
"name": null,
48+
"phone": null,
49+
"formatted_address": ""
50+
},
51+
"calculated_statement_descriptor": "AwesomeStore",
52+
"captured": true,
53+
"created": 1643280767,
54+
"currency": "usd",
55+
"customer": "cus_L2amf96J6ksaTQ",
56+
"description": "In-Person Payment for Order #205 for My WordPress Site",
57+
"destination": null,
58+
"dispute": null,
59+
"disputed": false,
60+
"failure_code": null,
61+
"failure_message": null,
62+
"fraud_details": [],
63+
"invoice": null,
64+
"level3": {
65+
"customer_reference": "205",
66+
"line_items": [
67+
{
68+
"discount_amount": 0,
69+
"product_code": "simple-payme",
70+
"product_description": "Simple Payments",
71+
"quantity": 1,
72+
"tax_amount": 0,
73+
"unit_cost": 1800
74+
}
75+
],
76+
"merchant_reference": "205",
77+
"shipping_amount": 0,
78+
"shipping_from_zip": "85120"
79+
},
80+
"livemode": false,
81+
"metadata": {
82+
"order_id": "205",
83+
"payment_type": "single",
84+
"paymentintent.storename": "My WordPress Site",
85+
"reader_ID": "STRM26138002546",
86+
"reader_model": "STRIPE_M2",
87+
"site_url": "https:\/\/store.example.com"
88+
},
89+
"on_behalf_of": null,
90+
"order": {
91+
"number": "205",
92+
"url": "https:\/\/store.example.com\/wp-admin\/post.php?post=205&action=edit",
93+
"customer_url": "admin.php?page=wc-admin&path=\/customers&filter=single_customer&customers=0",
94+
"subscriptions": []
95+
},
96+
"outcome": {
97+
"network_status": "approved_by_network",
98+
"reason": null,
99+
"risk_level": "not_assessed",
100+
"seller_message": "Payment complete.",
101+
"type": "authorized"
102+
},
103+
"paid": true,
104+
"payment_intent": "pi_3KMVap2EdyGr1FMV16atNgK9",
105+
"payment_method": "pm_1KMVas2EdyGr1FMVnleuPovE",
106+
"payment_method_details": {
107+
"card_present": {
108+
"amount_authorized": 1800,
109+
"brand": "visa",
110+
"cardholder_name": "CARDHOLDER\/VISA",
111+
"country": "US",
112+
"emv_auth_data": "8A023030",
113+
"exp_month": 3,
114+
"exp_year": 2023,
115+
"fingerprint": "gXsIWYqS9BLUBw0c",
116+
"funding": "credit",
117+
"generated_card": "pm_1KMVas2EdyGr1FMVa2Zzy6lj",
118+
"last4": "9969",
119+
"network": "visa",
120+
"overcapture_supported": false,
121+
"read_method": "contactless_emv",
122+
"receipt": {
123+
"account_type": "credit",
124+
"application_cryptogram": "6B75F6DE4A78CD0A",
125+
"application_preferred_name": "Stripe Credit",
126+
"authorization_code": null,
127+
"authorization_response_code": "3030",
128+
"cardholder_verification_method": "approval",
129+
"dedicated_file_name": "A000000003101001",
130+
"terminal_verification_results": "0000000000",
131+
"transaction_status_information": "0000"
132+
}
133+
},
134+
"type": "card_present"
135+
},
136+
"receipt_email": null,
137+
"receipt_number": null,
138+
"receipt_url": "https:\/\/pay.stripe.com\/receipts\/acct_1Jaefx2EdyGr1FMV\/ch_3KMVap2EdyGr1FMV1uKJEWtg\/rcpt_L2amKKvKwznzyj7VprDFvjt6LVfqpsN",
139+
"refunded": false,
140+
"refunds": {
141+
"object": "list",
142+
"data": [],
143+
"has_more": false,
144+
"total_count": 0,
145+
"url": "\/v1\/charges\/ch_3KMVap2EdyGr1FMV1uKJEWtg\/refunds"
146+
},
147+
"review": null,
148+
"shipping": null,
149+
"source": null,
150+
"source_transfer": null,
151+
"statement_descriptor": "AwesomeStore",
152+
"statement_descriptor_suffix": null,
153+
"status": "succeeded",
154+
"transfer_data": null,
155+
"transfer_group": null
156+
}
157+
}

0 commit comments

Comments
 (0)