Skip to content

Commit fa4b16e

Browse files
authored
3.0.0-beta.1 - Improved error handling + graceful recovery (#86)
* Update swift dependency, improve error handling * Add support for Android * Update snapshot * Improve tests + update snapshot * Add Swift test suite * Add Android test suite * Expose noop dismiss Android function * Swift - set platform on init * Add compare-snapshot script * Upgrade android + swift dependencies. Set platform * Improvements * Upgrade swift dep to beta.4 * Set checkoutSheet to nil on dismiss (android and swift) * Add error codes, always send code, update swift beta.5 * Post error events as hashmap strings, update tests
1 parent 224cbf2 commit fa4b16e

35 files changed

+867
-90
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
- run: |
4141
yarn module clean
4242
yarn module build
43-
./scripts/compare_snapshot
43+
yarn compare-snapshot
4444
4545
lint:
4646
name: Lint module + sample
@@ -52,6 +52,7 @@ jobs:
5252
uses: ./.github/actions/setup
5353

5454
- run: |
55+
yarn module build
5556
yarn module lint
5657
yarn sample lint
5758

modules/@shopify/checkout-sheet-kit/.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ disabled_rules:
33
- redundant_void_return
44
- todo
55
- switch_case_alignment
6+
- function_body_length
67

78
opt_in_rules:
89
- array_init

modules/@shopify/checkout-sheet-kit/RNShopifyCheckoutSheetKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Pod::Spec.new do |s|
2020
s.source_files = "ios/*.{h,m,mm,swift}"
2121

2222
s.dependency "React-Core"
23-
s.dependency "ShopifyCheckoutSheetKit", "~> 2.0.1"
23+
s.dependency "ShopifyCheckoutSheetKit", "~> 3.0.0-beta.5"
2424

2525
if fabric_enabled
2626
install_modules_dependencies(s)

modules/@shopify/checkout-sheet-kit/android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ ndkVersion=23.1.7779620
55
buildToolsVersion = "33.0.0"
66

77
# Version of Shopify Checkout SDK to use with React Native
8-
SHOPIFY_CHECKOUT_SDK_VERSION=2.0.1
8+
SHOPIFY_CHECKOUT_SDK_VERSION=3.0.0-beta.2

modules/@shopify/checkout-sheet-kit/android/src/main/java/com/shopify/reactnative/checkoutsheetkit/CustomCheckoutEventProcessor.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ of this software and associated documentation files (the "Software"), to deal
2727
import android.util.Log;
2828
import androidx.annotation.NonNull;
2929
import androidx.annotation.Nullable;
30+
3031
import com.shopify.checkoutsheetkit.*;
3132
import com.facebook.react.modules.core.DeviceEventManagerModule;
32-
import com.facebook.react.bridge.ReactApplicationContext;
3333
import com.facebook.react.bridge.WritableNativeMap;
34+
import com.facebook.react.bridge.ReactApplicationContext;
3435
import com.shopify.checkoutsheetkit.pixelevents.PixelEvent;
3536
import com.shopify.checkoutsheetkit.lifecycleevents.CheckoutCompletedEvent;
3637
import com.fasterxml.jackson.databind.ObjectMapper;
3738
import java.io.IOException;
39+
import java.util.HashMap;
40+
import java.util.Map;
3841

3942
public class CustomCheckoutEventProcessor extends DefaultCheckoutEventProcessor {
4043
private final ReactApplicationContext reactContext;
@@ -69,11 +72,42 @@ public void onWebPixelEvent(@NonNull PixelEvent event) {
6972

7073
@Override
7174
public void onCheckoutFailed(CheckoutException checkoutError) {
72-
WritableNativeMap error = new WritableNativeMap();
75+
try {
76+
String data = mapper.writeValueAsString(populateErrorDetails(checkoutError));
77+
sendEventWithStringData("error", data);
78+
} catch (IOException e) {
79+
Log.e("ShopifyCheckoutSheetKit", "Error processing checkout failed event", e);
80+
}
81+
}
7382

74-
error.putString("message", checkoutError.getErrorDescription());
83+
private Map<String, Object> populateErrorDetails(CheckoutException checkoutError) {
84+
Map<String, Object> errorMap = new HashMap();
85+
errorMap.put("__typename", getErrorTypeName(checkoutError));
86+
errorMap.put("message", checkoutError.getErrorDescription());
87+
errorMap.put("recoverable", checkoutError.isRecoverable());
88+
errorMap.put("code", checkoutError.getErrorCode());
7589

76-
sendEvent("error", error);
90+
if (checkoutError instanceof HttpException) {
91+
errorMap.put("statusCode", ((HttpException) checkoutError).getStatusCode());
92+
}
93+
94+
return errorMap;
95+
}
96+
97+
private String getErrorTypeName(CheckoutException error) {
98+
if (error instanceof CheckoutExpiredException) {
99+
return "CheckoutExpiredError";
100+
} else if (error instanceof ClientException) {
101+
return "CheckoutClientError";
102+
} else if (error instanceof HttpException) {
103+
return "CheckoutHTTPError";
104+
} else if (error instanceof ConfigurationException) {
105+
return "ConfigurationError";
106+
} else if (error instanceof CheckoutSheetKitException) {
107+
return "InternalError";
108+
} else {
109+
return "UnknownError";
110+
}
77111
}
78112

79113
@Override
@@ -89,7 +123,7 @@ private void sendEvent(String eventName, @Nullable WritableNativeMap params) {
89123

90124
private void sendEventWithStringData(String name, String data) {
91125
reactContext
92-
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
93-
.emit(name, data);
126+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
127+
.emit(name, data);
94128
}
95129
}

modules/@shopify/checkout-sheet-kit/android/src/main/java/com/shopify/reactnative/checkoutsheetkit/ShopifyCheckoutSheetKitModule.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ public class ShopifyCheckoutSheetKitModule extends ReactContextBaseJavaModule {
4646

4747
private final ReactApplicationContext reactContext;
4848

49+
private CheckoutSheetKitDialog checkoutSheet;
50+
4951
public ShopifyCheckoutSheetKitModule(ReactApplicationContext reactContext) {
5052
super(reactContext);
5153

5254
this.reactContext = reactContext;
55+
56+
ShopifyCheckoutSheetKit.configure(configuration -> {
57+
configuration.setPlatform(Platform.REACT_NATIVE);
58+
checkoutConfig = configuration;
59+
});
5360
}
5461

5562
@NonNull
@@ -82,12 +89,20 @@ public void present(String checkoutURL) {
8289
DefaultCheckoutEventProcessor checkoutEventProcessor = new CustomCheckoutEventProcessor(currentActivity,
8390
this.reactContext);
8491
currentActivity.runOnUiThread(() -> {
85-
ShopifyCheckoutSheetKit.present(checkoutURL, (ComponentActivity) currentActivity,
92+
checkoutSheet = ShopifyCheckoutSheetKit.present(checkoutURL, (ComponentActivity) currentActivity,
8693
checkoutEventProcessor);
8794
});
8895
}
8996
}
9097

98+
@ReactMethod
99+
public void dismiss() {
100+
if (checkoutSheet != null) {
101+
checkoutSheet.dismiss();
102+
checkoutSheet = null;
103+
}
104+
}
105+
91106
@ReactMethod
92107
public void preload(String checkoutURL) {
93108
Activity currentActivity = getCurrentActivity();

modules/@shopify/checkout-sheet-kit/ios/ShopifyCheckoutSheetKit.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ @interface RCT_EXTERN_MODULE(RCTShopifyCheckoutSheetKit, NSObject)
3131
/// Preload checkout
3232
RCT_EXTERN_METHOD(preload:(NSString *)checkoutURLString);
3333

34+
/// Dismiss checkout
35+
RCT_EXTERN_METHOD(dismiss);
36+
3437
/// Set configuration for checkout
3538
RCT_EXTERN_METHOD(setConfig:(NSDictionary *)configuration);
3639

modules/@shopify/checkout-sheet-kit/ios/ShopifyCheckoutSheetKit.swift

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
4040
return true
4141
}
4242

43+
override init() {
44+
ShopifyCheckoutSheetKit.configure {
45+
$0.platform = ShopifyCheckoutSheetKit.Platform.reactNative
46+
}
47+
48+
super.init()
49+
}
50+
4351
override func supportedEvents() -> [String]! {
4452
return ["close", "completed", "error", "pixel"]
4553
}
@@ -58,12 +66,60 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
5866
}
5967
}
6068

61-
func checkoutDidFail(error checkoutError: ShopifyCheckoutSheetKit.CheckoutError) {
62-
if hasListeners {
63-
let errorInfo: [String: Any] = [
64-
"message": checkoutError.localizedDescription
65-
]
66-
self.sendEvent(withName: "error", body: errorInfo)
69+
func shouldRecoverFromError(error: CheckoutError) -> Bool {
70+
return error.isRecoverable
71+
}
72+
73+
func checkoutDidFail(error: ShopifyCheckoutSheetKit.CheckoutError) {
74+
guard hasListeners else { return }
75+
76+
if case .checkoutExpired(let message, let code, let recoverable) = error {
77+
self.sendEvent(withName: "error", body: [
78+
"__typename": "CheckoutExpiredError",
79+
"message": message,
80+
"code": code.rawValue,
81+
"recoverable": recoverable
82+
])
83+
} else if case .checkoutUnavailable(let message, let code, let recoverable) = error {
84+
switch code {
85+
case .clientError(let clientErrorCode):
86+
self.sendEvent(withName: "error", body: [
87+
"__typename": "CheckoutClientError",
88+
"message": message,
89+
"code": clientErrorCode.rawValue,
90+
"recoverable": recoverable
91+
])
92+
case .httpError(let statusCode):
93+
self.sendEvent(withName: "error", body: [
94+
"__typename": "CheckoutHTTPError",
95+
"message": message,
96+
"code": "http_error",
97+
"statusCode": statusCode,
98+
"recoverable": recoverable
99+
])
100+
}
101+
} else if case .configurationError(let message, let code, let recoverable) = error {
102+
self.sendEvent(withName: "error", body: [
103+
"__typename": "ConfigurationError",
104+
"message": message,
105+
"code": code.rawValue,
106+
"recoverable": recoverable
107+
])
108+
} else if case .sdkError(let underlying, let recoverable) = error {
109+
var errorMessage = "\(underlying.localizedDescription)"
110+
self.sendEvent(withName: "error", body: [
111+
"__typename": "InternalError",
112+
"code": "unknown",
113+
"message": errorMessage,
114+
"recoverable": recoverable
115+
])
116+
} else {
117+
self.sendEvent(withName: "error", body: [
118+
"__typename": "UnknownError",
119+
"code": "unknown",
120+
"message": error.localizedDescription,
121+
"recoverable": error.isRecoverable
122+
])
67123
}
68124
}
69125

@@ -120,6 +176,12 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
120176
return controller
121177
}
122178

179+
@objc func dismiss() {
180+
DispatchQueue.main.async {
181+
self.checkoutSheet?.dismiss(animated: true)
182+
}
183+
}
184+
123185
@objc func present(_ checkoutURL: String) {
124186
DispatchQueue.main.async {
125187
if let url = URL(string: checkoutURL), let viewController = self.getCurrentViewController() {

modules/@shopify/checkout-sheet-kit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@shopify/checkout-sheet-kit",
33
"license": "MIT",
4-
"version": "2.0.1",
4+
"version": "3.0.0-beta.1",
55
"main": "lib/commonjs/index.js",
66
"types": "src/index.ts",
77
"source": "src/index.ts",

modules/@shopify/checkout-sheet-kit/package.snapshot.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"ios/ShopifyCheckoutSheetKit.swift",
1414
"lib/commonjs/context.js",
1515
"lib/commonjs/context.js.map",
16+
"lib/commonjs/errors.d.js",
17+
"lib/commonjs/errors.d.js.map",
1618
"lib/commonjs/events.d.js",
1719
"lib/commonjs/events.d.js.map",
1820
"lib/commonjs/index.d.js",
@@ -23,6 +25,8 @@
2325
"lib/commonjs/pixels.d.js.map",
2426
"lib/module/context.js",
2527
"lib/module/context.js.map",
28+
"lib/module/errors.d.js",
29+
"lib/module/errors.d.js.map",
2630
"lib/module/events.d.js",
2731
"lib/module/events.d.js.map",
2832
"lib/module/index.d.js",
@@ -37,6 +41,7 @@
3741
"lib/typescript/src/index.d.ts.map",
3842
"package.json",
3943
"src/context.tsx",
44+
"src/errors.d.ts",
4045
"src/events.d.ts",
4146
"src/index.d.ts",
4247
"src/index.ts",

0 commit comments

Comments
 (0)