npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@geideasdk/payment-sdk-react-native

v0.0.1

Published

A comprehensive guide for integrating the GDPaymentSDK into React Native applications for both iOS and Android platforms.

Readme

GDPaymentSDK React Native Integration

A comprehensive guide for integrating the GDPaymentSDK into React Native applications for both iOS and Android platforms.

Table of Contents


Prerequisites

Before starting the integration, ensure you have:

  • React Native development environment configured
  • Node.js and npm/yarn installed
  • Android Studio for Android development
  • Xcode for iOS development (macOS only)
  • GDPaymentSDK files:
    • Android: PGW-SDK-1.0.0.aar, PGW-SDK-1.0.0.module, PGW-SDK-1.0.0.pom
    • iOS: CardScan.xcframework, GeideaPaymentSDK.xcframework

Project Setup

1. Create a New React Native Project

npx react-native init GeideaPaymentApp
cd GeideaPaymentApp

2. Install Required Dependencies

npm install react-native-safe-area-context

3. Create Project Structure

GeideaPaymentApp/
├── App.tsx
├── native/
│   └── GeideaBridge.ts
├── android/
│   └── app/
│       ├── maven-repo/
│       │   └── net/
│       │       └── geidea/
│       │           └── PGW-SDK/
│       │               └── 1.0.0/
│       └── src/
│           └── main/
│               └── java/
│                   └── net/
│                       └── geidea/
│                           └── paymentsdk/
│                               ├── GeideaBridgeModule.kt
│                               └── GeideaBridgePackage.kt
└── ios/
    ├── Frameworks/
    │   ├── CardScan.xcframework
    │   └── GeideaPaymentSDK.xcframework
    └── GeideaPaymentApp/
        ├── GeideaBridge.m
        └── GeideaBridge.swift

Android Integration

Step 1: Add SDK Files to Maven Repository

  1. Create the directory structure: android/app/maven-repo/net/geidea/PGW-SDK/1.0.0/
  2. Copy the following files into this directory:
    • PGW-SDK-1.0.0.aar
    • PGW-SDK-1.0.0.module
    • PGW-SDK-1.0.0.pom

Step 2: Configure Build Gradle (Project Level)

Edit android/build.gradle:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven {
            url "$rootDir/app/maven-repo"
        }
    }
}

Step 3: Configure Build Gradle (App Level)

Edit android/app/build.gradle:

dependencies {
    implementation("com.facebook.react:react-android")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
    implementation("net.geidea:PGW-SDK:1.0.0")
    implementation("com.google.android.material:material:1.9.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("androidx.core:core-ktx:1.10.1")

    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}

Step 4: Create Bridge Module

Create android/app/src/main/java/net/geidea/paymentsdk/GeideaBridgeModule.kt:

package net.geidea.paymentsdk

import android.app.Activity
import android.content.Intent
import com.facebook.react.bridge.*
import net.geidea.sdk.sdk.*
import android.util.Log

class GeideaBridgeModule(private val reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {

    private var currentPromise: Promise? = null

    private val activityEventListener = object : ActivityEventListener {
        override fun onActivityResult(
            activity: Activity,
            requestCode: Int,
            resultCode: Int,
            data: Intent?
        ) {
            // SDK handles results via listener
        }

        override fun onNewIntent(intent: Intent) {
            // Not needed
        }
    }

    init {
        reactContext.addActivityEventListener(activityEventListener)
    }

    override fun getName(): String = "GeideaBridge"

    @ReactMethod
    fun startWithConfig(params: ReadableMap, promise: Promise) {
        if (currentPromise != null) {
            promise.reject("E_IN_PROGRESS", "Payment already in progress")
            return
        }

        val sessionId = params.getString("sessionId")
        if (sessionId.isNullOrEmpty()) {
            promise.reject("E_ARGS", "sessionId is required")
            return
        }

        val language = mapLanguage(params.getString("language"))
        val isSandbox = params.getString("environment") == "sandbox"
        val region = mapRegion(params.getString("region"))

        currentPromise = promise

        try {
            val activity = reactContext.currentActivity
            if (activity == null) {
                currentPromise = null
                promise.reject("E_ACTIVITY", "Activity not found")
                return
            }

            val theme = SDKTheme()
            val config = GDPaymentSDKConfiguration(
                theme = theme,
                sessionId = sessionId,
                language = language,
                isSandbox = isSandbox,
                region = region
            )

            val resultListener = object : GDPaymentResultListener {
                override fun onPaymentCompleted(result: GDPaymentResult) {
                    val promise = currentPromise ?: return
                    currentPromise = null

                    val resultMap = convertPaymentResultToMap(result)
                    val response = Arguments.createMap().apply {
                        putString("status", "completed")
                        putMap("result", resultMap)
                    }
                    promise.resolve(response)
                }

                override fun onPaymentFailure(error: GDPaymentError) {
                    val promise = currentPromise ?: return
                    currentPromise = null

                    val errorMap = convertPaymentErrorToMap(error)
                    val errorCode = errorMap.getString("code") ?: "E_PAYMENT_FAILED"
                    val errorMessage = errorMap.getString("message") ?: "Payment failed"
                    promise.reject(errorCode, errorMessage, errorMap)
                }

                override fun onPaymentCanceled() {
                    val promise = currentPromise ?: return
                    currentPromise = null

                    val response = Arguments.createMap().apply {
                        putString("status", "canceled")
                    }
                    promise.resolve(response)
                }
            }

            val sdk = GDPaymentSDK.sharedInstance()
            sdk.setPaymentCallback(resultListener)

            val presentationStyle = SDKPresentationStyle.Push()
            sdk.start(config, activity, presentationStyle)

        } catch (e: Exception) {
            currentPromise = null
            promise.reject("E_SDK_START", "Failed to start SDK: ${e.localizedMessage}", e)
        }
    }

    private fun mapLanguage(raw: String?): SDKLanguage {
        return when (raw?.lowercase()) {
            "ar", "arabic", "ar-eg", "ar-sa" -> SDKLanguage.ARABIC
            else -> SDKLanguage.ENGLISH
        }
    }

    private fun mapRegion(raw: String?): REGION {
        return when (raw?.lowercase()) {
            "sa", "ksa", "saudi", "saudi arabia" -> REGION.KSA
            "ae", "uae", "emirates" -> REGION.UAE
            "eg", "egypt", "egy" -> REGION.EGY
            else -> REGION.EGY
        }
    }
    
    private fun convertPaymentResultToMap(result: GDPaymentResult): WritableMap {
        val map = Arguments.createMap()

        try {
            result.orderId?.let { map.putString("orderId", it) } ?: map.putNull("orderId")
            result.tokenId?.let { map.putString("tokenId", it) } ?: map.putNull("tokenId")
            result.agreementId?.let { map.putString("agreementId", it) } ?: map.putNull("agreementId")

            result.paymentMethod?.let { pm ->
                val paymentMethodMap = Arguments.createMap()
                pm.type?.let { paymentMethodMap.putString("type", it) }
                pm.brand?.let { paymentMethodMap.putString("brand", it) }
                pm.cardholderName?.let { paymentMethodMap.putString("cardholderName", it) }
                pm.maskedCardNumber?.let { paymentMethodMap.putString("maskedCardNumber", it) }
                pm.wallet?.let { paymentMethodMap.putString("wallet", it) } ?: paymentMethodMap.putNull("wallet")

                pm.expiryDate?.let { expiry ->
                    val expiryMap = Arguments.createMap()
                    expiryMap.putInt("month", expiry.month ?: 0)
                    expiryMap.putInt("year", expiry.year ?: 0)
                    paymentMethodMap.putMap("expiryDate", expiryMap)
                } ?: paymentMethodMap.putNull("expiryDate")

                map.putMap("paymentMethod", paymentMethodMap)
            } ?: map.putNull("paymentMethod")

        } catch (e: Exception) {
            map.putString("_parseError", e.localizedMessage ?: "Unknown error")
        }

        return map
    }

    private fun convertPaymentErrorToMap(error: GDPaymentError): WritableMap {
        val map = Arguments.createMap()
        try {
            error.javaClass.declaredFields.forEach { field ->
                field.isAccessible = true
                val value = field.get(error)
                when (value) {
                    null -> map.putNull(field.name)
                    is String -> map.putString(field.name, value)
                    is Int -> map.putInt(field.name, value)
                    is Long -> map.putDouble(field.name, value.toDouble())
                    is Double -> map.putDouble(field.name, value)
                    is Float -> map.putDouble(field.name, value.toDouble())
                    is Boolean -> map.putBoolean(field.name, value)
                    else -> map.putString(field.name, value.toString())
                }
            }
        } catch (e: Exception) {
            map.putString("code", "E_PAYMENT_ERROR")
            map.putString("message", e.localizedMessage)
        }
        return map
    }
}

Step 5: Create Bridge Package

Create android/app/src/main/java/net/geidea/paymentsdk/GeideaBridgePackage.kt:

package net.geidea.paymentsdk

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class GeideaBridgePackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(GeideaBridgeModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

Step 6: Register Package

Edit android/app/src/main/java/com/geideapaymentapp/MainApplication.java (or .kt):

import net.geidea.paymentsdk.GeideaBridgePackage; // Add this import

@Override
protected List<ReactPackage> getPackages() {
    List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new GeideaBridgePackage()); // Add this line
    return packages;
}

iOS Integration

Step 1: Add Framework Files

  1. Create a Frameworks folder inside the ios directory
  2. Copy CardScan.xcframework and GeideaPaymentSDK.xcframework into this folder

Step 2: Link Frameworks in Xcode

  1. Open ios/GeideaPaymentApp.xcworkspace in Xcode
  2. Select your project in the Project Navigator
  3. Select your app target
  4. Go to General tab
  5. Scroll to Frameworks, Libraries, and Embedded Content
  6. Click the + button and add:
    • CardScan.xcframework
    • GeideaPaymentSDK.xcframework
  7. Set both to Embed & Sign

Step 3: Create Bridging Header

If you don't have a bridging header, create one:

  1. In Xcode, go to File → New → File
  2. Select Header File
  3. Name it GeideaPaymentApp-Bridging-Header.h
  4. Add the following content:
#import <React/RCTBridgeModule.h>
  1. In Build Settings, set Objective-C Bridging Header to GeideaPaymentApp/GeideaPaymentApp-Bridging-Header.h

Step 4: Create Objective-C Bridge File

Create ios/GeideaPaymentApp/GeideaBridge.m:

#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(GeideaBridge, NSObject)

RCT_EXTERN_METHOD(startWithConfig:(NSDictionary *)params
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)

+ (BOOL)requiresMainQueueSetup
{
  return YES;
}

@end

Step 5: Create Swift Implementation

Create ios/GeideaPaymentApp/GeideaBridge.swift:

import Foundation
import React
import GeideaPaymentSDK
import UIKit

@objc(GeideaBridge)
class GeideaBridge: NSObject {
  
  @objc static func requiresMainQueueSetup() -> Bool { true }
  
  private var currentDelegate: RNGeideaDelegate?
  
  @objc(startWithConfig:resolver:rejecter:)
  func startWithConfig(
    _ params: NSDictionary,
    resolver resolve: @escaping RCTPromiseResolveBlock,
    rejecter reject: @escaping RCTPromiseRejectBlock
  ) {
    guard let sessionId = params["sessionId"] as? String, !sessionId.isEmpty else {
      return reject("E_ARGS", "sessionId is required", nil)
    }
    
    let lang   = mapLanguage(params["language"] as? String)
    let env    = mapEnvironment(params["environment"] as? String)
    let region = mapRegion(params["region"] as? String)
    
    let cfg = GeideaPaymentSDK.GDPaymentSDKConfiguration(
      sessionId: sessionId,
      applePayConfig: ApplePayConfigurations(merchantId: "merchant.com.geidea.sdk.payment.mock"),
      language: lang,
      environmentType: env,
      region: region
    )
    
    Task { @MainActor in
      let delegate = RNGeideaDelegate(
        onCompleted: { result in
          self.currentDelegate = nil
          resolve([
            "status": "completed",
            "result": result
          ])
        },
        onCanceled: {
          self.currentDelegate = nil
          resolve([
            "status": "canceled"
          ])
        },
        onFailed: { code, message, error in
          self.currentDelegate = nil
          reject(code, message, error)
        }
      )
      
      self.currentDelegate = delegate
      
      do {
        let instance = GeideaPaymentSDK.GDPaymentSDK.sharedInstance()
        try instance.start(configuration: cfg, delegate: delegate)
      } catch {
        self.currentDelegate = nil
        reject("E_SDK_START", "Failed to start SDK: \(error.localizedDescription)", error as NSError)
      }
    }
  }
  
  private func mapLanguage(_ raw: String?) -> GeideaPaymentSDK.AppLanguage {
    if let key = raw?.lowercased(), let val = GeideaPaymentSDK.AppLanguage(rawValue: key) {
      return val
    }
    switch (raw ?? "").lowercased() {
      case "ar", "arabic", "ar-eg", "ar-sa": return .arabic
      default: return .english
    }
  }
  
  private func mapEnvironment(_ raw: String?) -> GeideaPaymentSDK.EnvironmentType {
    if let key = raw?.lowercased(), let val = GeideaPaymentSDK.EnvironmentType(rawValue: key) {
      return val
    }
    switch (raw ?? "").lowercased() {
      case "production": return .production
      default: return .sandbox
    }
  }
  
  private func mapRegion(_ raw: String?) -> GeideaPaymentSDK.Region {
    if let s = raw?.lowercased(), let v = GeideaPaymentSDK.Region(rawValue: s) {
      return v
    }
    switch (raw ?? "").lowercased() {
      case "sa", "ksa": return .ksa
      case "ae", "uae": return .uae
      case "eg", "egypt": return .egy
      default: return .egy
    }
  }
}

final class RNGeideaDelegate: NSObject, GeideaPaymentSDK.GDSDKProtocol {
  
  private let onCompleted: (_ result: [String: Any]) -> Void
  private let onCanceled: () -> Void
  private let onFailed: (_ code: String, _ message: String, _ error: NSError?) -> Void
  
  init(onCompleted: @escaping (_ result: [String: Any]) -> Void,
       onCanceled: @escaping () -> Void,
       onFailed: @escaping (_ code: String, _ message: String, _ error: NSError?) -> Void) {
    self.onCompleted = onCompleted
    self.onCanceled = onCanceled
    self.onFailed = onFailed
  }
  
  @MainActor
  func onPaymentCompleted(result: GeideaPaymentSDK.GDPaymentResult) {
    onCompleted(Self.flattenToDictionary(result))
  }
  
  @MainActor
  func onPaymentFailed(error: GeideaPaymentSDK.GDSDKError) {
    let dict = Self.flattenToDictionary(error)
    let code = (dict["code"] as? String) ?? "E_PAYMENT_FAILED"
    let msg  = (dict["message"] as? String)
    ?? (dict["localizedDescription"] as? String)
    ?? "Payment failed"
    onFailed(code, msg, NSError(domain: code, code: -1, userInfo: dict))
  }
  
  @MainActor
  func onPaymentCanceled() {
    onCanceled()
  }
  
  private static func flattenToDictionary(_ value: Any) -> [String: Any] {
    if let d = value as? [String: Any] { return d }
    if let e = value as? NSError {
      return ["code": e.domain, "message": e.localizedDescription]
    }
    let m = Mirror(reflecting: value)
    var out: [String: Any] = ["description": String(describing: value)]
    for child in m.children {
      if let label = child.label {
        out[label] = unwrap(child.value)
      }
    }
    return out
  }
  
  private static func unwrap(_ any: Any) -> Any {
    let mirror = Mirror(reflecting: any)
    if mirror.displayStyle != .optional { return any }
    if let child = mirror.children.first { return child.value }
    return NSNull()
  }
}

React Native Bridge Implementation

Create TypeScript Bridge

Create native/GeideaBridge.ts:

import { NativeModules } from 'react-native';

type Language = 'en' | 'ar';
type Environment = 'sandbox' | 'production';
type Region = 'egypt' | 'ksa' | 'uae';

type StartWithConfigOptions = {
  sessionId: string;
  language?: Language;
  environment?: Environment;
  region?: Region;
};

type PaymentMethod = {
  type?: string;
  brand?: string;
  maskedCardNumber?: string;
  cardholderName?: string;
  wallet?: string;
  expiryDate?: {
    month: number;
    year: number;
  };
};

type GeideaResult = {
  status: 'completed' | 'canceled';
  result?: {
    orderId?: string;
    tokenId?: string;
    agreementId?: string;
    paymentMethod?: PaymentMethod;
  };
};

const { GeideaBridge } = NativeModules as {
  GeideaBridge: { startWithConfig(opts: StartWithConfigOptions): Promise<GeideaResult> };
};

let inFlight: Promise<GeideaResult> | null = null;

export function payWithGeidea(opts: StartWithConfigOptions): Promise<GeideaResult> {
  if (!opts?.sessionId) return Promise.reject(new Error('sessionId is required'));
  if (inFlight) return inFlight;
  
  inFlight = GeideaBridge.startWithConfig({
    language: 'en',
    environment: 'sandbox',
    region: 'egypt',
    ...opts,
  }).finally(() => {
    inFlight = null;
  });
  
  return inFlight;
}

Testing the Integration

Android Testing

npx react-native run-android

iOS Testing

cd ios
pod install
cd ..
npx react-native run-ios

Test Flow

  1. Select region (Egypt, KSA, or UAE)
  2. Select environment (Testing or Pre-Production)
  3. Select language (English or Arabic)
  4. Enter a valid session ID
  5. Tap the PAY button
  6. Complete the payment flow in the SDK
  7. Verify the response is displayed correctly

Support

For issues or questions, please refer to the official GDPaymentSDK documentation or contact Geidea support.


License

This integration guide is provided as-is for developers implementing the GDPaymentSDK in React Native applications.