Skip to content

Commit

Permalink
feat: add support for all interface attributes (#3)
Browse files Browse the repository at this point in the history
* Repleace release-it with auto

* Update ci.yml

* Set access public

* Create empty CHANGELOG.md file

* Add scan job with trivy

* Fix CI actions

* Upgrade babel-plugin-module-resolver to fix json5 vulnerability

* Skip scan node_modules directory

* Fix buyerId prop type

* Pass debugMode prop

* Update example app README with cache reset info

* Update default GR4VY_ID in .env.example

* Add externalIdentifier prop support

* Add other props and attempt to properly type paymentSource

* Add cartItems prop

* Add cartItems typing on the js side

* Add paymentSource and cartItems type conversions

* Remove previous cartItems map

* Fix Android params

* Use Native Event Emitter in iOS

* Attempt to add the same `onEvent` logging to the Android part

* Fix events handling in android

* Remove any onEvent listener before adding a new one

* Clean up android code

* Set default buyerId to null in example app
  • Loading branch information
luca-gr4vy authored Apr 18, 2023
1 parent bb5f025 commit 7e49ed1
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.gr4vy.embedreactnative;

import javax.annotation.Nullable;

import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;

public class EmbedReactNativeEvents {
public static void sendEvent(
ReactContext reactContext,
String eventName,
@Nullable WritableMap params
) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}

private int listenerCount = 0;

@ReactMethod
public void addListener(String eventName) {
if (listenerCount == 0) {
// Set up any upstream listeners or background tasks as necessary
}

listenerCount += 1;
}

@ReactMethod
public void removeListeners(Integer count) {
listenerCount -= count;
if (listenerCount == 0) {
// Remove upstream listeners, stop unnecessary background tasks
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;

import com.gr4vy.embedreactnative.EmbedReactNativeEvents;

import android.app.Activity;
import android.content.Intent;
import android.util.Log;
Expand All @@ -26,8 +30,6 @@ public class EmbedReactNativeModule extends ReactContextBaseJavaModule {
static final String EXTRA_COUNTRY = "EXTRA_COUNTRY";
static final String EXTRA_BUYER_ID = "EXTRA_BUYER_ID";
private static final int GR4VY_PAYMENT_SHEET_REQUEST = 1;
private Callback successCallback;
private Callback errorCallback;

public EmbedReactNativeModule(ReactApplicationContext context) {
super(context);
Expand All @@ -37,6 +39,7 @@ public EmbedReactNativeModule(ReactApplicationContext context) {
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (requestCode == GR4VY_PAYMENT_SHEET_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
String event = data.getStringExtra(Gr4vyActivity.EXTRA_EVENT);
String error = data.getStringExtra(Gr4vyActivity.EXTRA_ERROR);

if (error == null) {
Expand All @@ -46,15 +49,28 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode,
String paymentMethodId = data.getStringExtra(Gr4vyActivity.EXTRA_PAYMENT_METHOD_ID);

WritableMap result = Arguments.createMap();
result.putBoolean("success", success);
result.putString("status", status);
result.putString("transactionId", transactionId);
result.putString("paymentMethodId", paymentMethodId);
result.putString("name", event);

WritableMap resultData = Arguments.createMap();
resultData.putBoolean("success", success);
resultData.putString("status", status);
resultData.putString("transactionId", transactionId);
resultData.putString("paymentMethodId", paymentMethodId);

successCallback.invoke(result);
result.putMap("data", resultData);

EmbedReactNativeEvents.sendEvent(context, "onEvent", result);
}
else {
errorCallback.invoke(error);
WritableMap result = Arguments.createMap();
result.putString("name", event);

WritableMap resultData = Arguments.createMap();
resultData.putString("message", error);

result.putMap("data", resultData);

EmbedReactNativeEvents.sendEvent(context, "onEvent", result);
}
} else if (resultCode == Activity.RESULT_CANCELED) {
// Do nothing
Expand All @@ -80,23 +96,26 @@ public void showPaymentSheet(
String currency,
String country,
String buyerId,
String externalIdentifier,
String store,
String display,
String intent,
ReadableMap metadata,
String paymentSource,
ReadableArray cartItems,
String environment,
Callback errorCallback,
Callback successCallback) {
Boolean debugMode) {
Log.d("Gr4vy", "showPaymentSheet()");

ReactApplicationContext context = getReactApplicationContext();
Intent intent = new Intent(context, Gr4vyActivity.class);

this.successCallback = successCallback;
this.errorCallback = errorCallback;
Intent androidIntent = new Intent(context, Gr4vyActivity.class);

intent.putExtra(EXTRA_TOKEN, token);
intent.putExtra(EXTRA_AMOUNT, Integer.valueOf(amount.intValue()));
intent.putExtra(EXTRA_CURRENCY, currency);
intent.putExtra(EXTRA_COUNTRY, country);
intent.putExtra(EXTRA_BUYER_ID, buyerId);
androidIntent.putExtra(EXTRA_TOKEN, token);
androidIntent.putExtra(EXTRA_AMOUNT, Integer.valueOf(amount.intValue()));
androidIntent.putExtra(EXTRA_CURRENCY, currency);
androidIntent.putExtra(EXTRA_COUNTRY, country);
androidIntent.putExtra(EXTRA_BUYER_ID, buyerId);

context.startActivityForResult(intent, GR4VY_PAYMENT_SHEET_REQUEST, null);
context.startActivityForResult(androidIntent, GR4VY_PAYMENT_SHEET_REQUEST, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
public class Gr4vyActivity extends ComponentActivity implements Gr4vyResultHandler {
private Gr4vySDK gr4vySDK;
private final ActivityResultRegistry activityResultRegistry = this.getActivityResultRegistry();
static final String EXTRA_EVENT = "EXTRA_EVENT";
static final String EXTRA_ERROR = "EXTRA_ERROR";
static final String EXTRA_SUCCESS = "EXTRA_SUCCESS";
static final String EXTRA_STATUS = "EXTRA_STATUS";
Expand All @@ -32,6 +33,8 @@ public class Gr4vyActivity extends ComponentActivity implements Gr4vyResultHandl
String currency;
String country;

Boolean sdkLaunched = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -50,6 +53,10 @@ protected void onCreate(Bundle savedInstanceState) {
public void onStart() {
super.onStart();

if (sdkLaunched) {
return;
}

gr4vySDK.launch(
this,
token,
Expand All @@ -64,6 +71,8 @@ public void onStart() {
null,
PaymentSource.NOT_SET,
null);

sdkLaunched = true;
}

@Override
Expand All @@ -79,6 +88,7 @@ public void onGr4vyResult(@NonNull Gr4vyResult gr4vyResult) {
Log.d("Gr4vy", "transactionId: " + ((Gr4vyResult.TransactionCreated) gr4vyResult).getTransactionId());
Log.d("Gr4vy", "paymentMethodId: " + ((Gr4vyResult.TransactionCreated) gr4vyResult).getPaymentMethodId());

data.putExtra(EXTRA_EVENT, "transactionCreated");
data.putExtra(EXTRA_SUCCESS, true);
data.putExtra(EXTRA_STATUS, ((Gr4vyResult.TransactionCreated) gr4vyResult).getStatus());
data.putExtra(EXTRA_TRANSACTION_ID, ((Gr4vyResult.TransactionCreated) gr4vyResult).getTransactionId());
Expand All @@ -94,6 +104,7 @@ else if (gr4vyResult instanceof Gr4vyResult.TransactionFailed) {
Log.d("Gr4vy", "transactionId: " + ((Gr4vyResult.TransactionFailed) gr4vyResult).getTransactionId());
Log.d("Gr4vy", "paymentMethodId: " + ((Gr4vyResult.TransactionFailed) gr4vyResult).getPaymentMethodId());

data.putExtra(EXTRA_EVENT, "transactionFailed");
data.putExtra(EXTRA_SUCCESS, true);
data.putExtra(EXTRA_STATUS, ((Gr4vyResult.TransactionFailed) gr4vyResult).getStatus());
data.putExtra(EXTRA_TRANSACTION_ID, ((Gr4vyResult.TransactionFailed) gr4vyResult).getTransactionId());
Expand All @@ -106,6 +117,7 @@ else if (gr4vyResult instanceof Gr4vyResult.GeneralError) {

Log.d("Gr4vy", "error: " + ((Gr4vyResult.GeneralError) gr4vyResult).getReason());

data.putExtra(EXTRA_EVENT, "generalError");
data.putExtra(EXTRA_ERROR, ((Gr4vyResult.GeneralError) gr4vyResult).getReason());

setResult(RESULT_OK, data);
Expand All @@ -114,6 +126,7 @@ else if (gr4vyResult instanceof Gr4vyResult.GeneralError) {
Log.d("Gr4vy", "An unknown error has occurred.");
}

sdkLaunched = false;
finish();
}
}
2 changes: 1 addition & 1 deletion example/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
GR4VY_ID=dev
GR4VY_ID=spider
TOKEN=
2 changes: 1 addition & 1 deletion example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ Simple React Native application demonstrating the usage of Embed.

Before launching the example application, run `yarn` from the project's root to install all the required dependencies.

Make sure `GR4VY_ID` and `TOKEN` are set in the `.env` file (you can copy `.env.example` and update accordingly).
Make sure `GR4VY_ID` and `TOKEN` are set in the `.env` file (you can copy `.env.example` and update accordingly). If you later change any of these variables, you have to reset Metro's cache by running `yarn start --reset-cache`, otherwise the old values would still be used.

Running `yarn ios` or `yarn android` from the package folder will start the app on either an iOS or Android simulator.
51 changes: 31 additions & 20 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,41 @@ import { SafeAreaView, StatusBar } from 'react-native'
import { Checkout } from './components/Checkout'
import EmbedReactNative, {
EmbedReactNativeEventEmitter,
Gr4vyTransactionResult,
Gr4vyPaymentMethod,
Gr4vyEvent,
} from '@gr4vy/embed-react-native'
import { total } from './constants/data'

const onPaymentMethodSelected = (paymentMethod: Gr4vyPaymentMethod) => {
console.log('onPaymentMethodSelected', paymentMethod)
}

const config = {
gr4vyId: `${GR4VY_ID}`,
env: 'sandbox',
token: `${TOKEN}`,
amount: total,
currency: 'USD',
country: 'US',
buyerId: null,
externalIdentifier: null,
store: 'ask',
display: 'all',
intent: 'capture',
metadata: {},
paymentSource: null,
cartItems: null,
debugMode: true,
}

const onEvent = (event: Gr4vyEvent) => {
const { name, data } = event
console[name === 'generalError' ? 'error' : 'log'](name, data)
}

function App(): JSX.Element {
const onPaymentMethodSelectedSubscription =
EmbedReactNativeEventEmitter.addListener(
'onPaymentMethodSelected',
onPaymentMethodSelected
)
const onEventSubscription = EmbedReactNativeEventEmitter.addListener(
'onEvent',
(event: Gr4vyEvent) => {
onEvent(event)
onEventSubscription.remove()
}
)

const handleCheckout = () => {
EmbedReactNative.showPaymentSheet(
Expand All @@ -36,16 +47,16 @@ function App(): JSX.Element {
config.amount,
config.currency,
config.country,
null,
config.buyerId,
config.externalIdentifier,
config.store,
config.display,
config.intent,
config.metadata,
config.paymentSource,
config.cartItems,
config.env,
(error: string) => {
console.error(error)
onPaymentMethodSelectedSubscription.remove()
},
(transactionResult: Gr4vyTransactionResult) => {
console.log(transactionResult)
onPaymentMethodSelectedSubscription.remove()
}
config.debugMode
)
}

Expand Down
2 changes: 2 additions & 0 deletions ios/EmbedReactNative-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTUtils.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTConvert.h>
#import "RCTCartItem.h"
17 changes: 15 additions & 2 deletions ios/EmbedReactNative.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTConvert.h>
#import <React/RCTEventEmitter.h>
#import <Foundation/Foundation.h>
#import "RCTCartItem.h"

@interface RCT_EXTERN_MODULE(EmbedReactNative, NSObject)

Expand All @@ -10,11 +13,21 @@ @interface RCT_EXTERN_MODULE(EmbedReactNative, NSObject)
currency:(NSString *)currency
country:(NSString *)country
buyerId:(NSString *)buyerId
externalIdentifier:(NSString *)externalIdentifier
store:(NSString *)store
display:(NSString *)display
intent:(NSString *)intent
metadata:(NSDictionary *)metadata
paymentSource:(NSString *)paymentSource
cartItems:(NSArray<RCTCartItem *> *)cartItems
environment:(NSString *)environment
errorCallback:(RCTResponseSenderBlock)errorCallback
successCallback:(RCTResponseSenderBlock)successCallback)
debugMode:(BOOL)debugMode)
@end

@interface RCT_EXTERN_MODULE(EmbedReactNativeEvents, RCTEventEmitter)
RCT_EXTERN_METHOD(supportedEvents)
@end

@implementation RCTConvert (RCTCartItemArray)
RCT_ARRAY_CONVERTER(RCTCartItem)
@end
Loading

0 comments on commit 7e49ed1

Please sign in to comment.