├── .npmignore ├── index.js ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ └── layout │ │ │ └── nfcview.xml │ │ └── java │ │ └── com │ │ └── jackbayliss │ │ └── nfcreader │ │ ├── NfcCardReaderPackage.java │ │ ├── NfcCardReaderModule.java │ │ └── NfcCardReaderActivity.java ├── README.md └── build.gradle ├── .gitignore ├── .github └── FUNDING.yml ├── LICENSE ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | working-example -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { NativeModules } from 'react-native'; 2 | module.exports = NativeModules.NfcCardReader; -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # node.js 6 | # 7 | node_modules/ 8 | npm-debug.log 9 | yarn-error.log 10 | 11 | # Xcode 12 | # 13 | build/ 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | *.xccheckout 24 | *.moved-aside 25 | DerivedData 26 | *.hmap 27 | *.ipa 28 | *.xcuserstate 29 | project.xcworkspace 30 | 31 | # Android/IntelliJ 32 | # 33 | build/ 34 | .idea 35 | .gradle 36 | local.properties 37 | *.iml 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jackbayliss] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: jackbayliss 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm: 5 | 6 | 1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed 7 | 2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK 8 | ``` 9 | ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle 10 | sdk.dir=/Users/{username}/Library/Android/sdk 11 | ``` 12 | 3. Delete the `maven` folder 13 | 4. Run `./gradlew installArchives` 14 | 5. Verify that latest set of generated files is in the maven folder with the correct version number 15 | -------------------------------------------------------------------------------- /android/src/main/res/layout/nfcview.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/jackbayliss/nfcreader/NfcCardReaderPackage.java: -------------------------------------------------------------------------------- 1 | package com.jackbayliss.nfcreader; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.uimanager.ViewManager; 11 | import com.facebook.react.bridge.JavaScriptModule; 12 | 13 | public class NfcCardReaderPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new NfcCardReaderModule(reactContext)); 17 | } 18 | 19 | @Override 20 | public List createViewManagers(ReactApplicationContext reactContext) { 21 | return Collections.emptyList(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jack Bayliss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-nfc-card-reader", 3 | "title": "React Native Nfc Card Reader", 4 | "version": "1.0.6", 5 | "description": "An NFC reader specifically for credit/debit cards.", 6 | "main": "index.js", 7 | "files": [ 8 | "README.md", 9 | "android", 10 | "index.js" 11 | ], 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/jackbayliss/react-native-nfc-card-reader.git", 18 | "baseUrl": "https://github.com/jackbayliss/react-native-nfc-card-reader" 19 | }, 20 | "keywords": [ 21 | "react-native", "nfc-card-reader","nfc-credit-card","nfc card" 22 | ], 23 | "author": { 24 | "name": "Jack Bayliss", 25 | "email": "jack@jackbayliss.com" 26 | }, 27 | "homepage": "https://github.com/jackbayliss/react-native-nfc-card-reader", 28 | "license": "MIT", 29 | "licenseFilename": "LICENSE", 30 | "readmeFilename": "README.md", 31 | "peerDependencies": { 32 | "react": ">=16.8.1", 33 | "react-native": ">=0.60.0-rc.0 <1.0.x" 34 | }, 35 | "devDependencies": { 36 | "react": "^16.9.0", 37 | "react-native": "^0.62.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android/src/main/java/com/jackbayliss/nfcreader/NfcCardReaderModule.java: -------------------------------------------------------------------------------- 1 | package com.jackbayliss.nfcreader; 2 | 3 | import android.content.Intent; 4 | import android.app.Activity; 5 | import android.nfc.NfcAdapter; 6 | import android.util.Log; 7 | 8 | import com.facebook.react.bridge.ActivityEventListener; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | import com.facebook.react.bridge.Callback; 13 | import com.facebook.react.bridge.WritableMap; 14 | import com.facebook.react.bridge.Arguments; 15 | 16 | 17 | public class NfcCardReaderModule extends ReactContextBaseJavaModule implements ActivityEventListener { 18 | 19 | private static final String TAG = "NfcCardReaderActivity"; 20 | private static final int INTENT = 900; 21 | private final ReactApplicationContext reactContext; 22 | 23 | private Callback mNfcCallback = null; 24 | 25 | public NfcCardReaderModule(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | this.reactContext = reactContext; 28 | this.reactContext.addActivityEventListener(this); 29 | 30 | } 31 | 32 | @Override 33 | public String getName() { 34 | return "NfcCardReader"; 35 | } 36 | 37 | @ReactMethod 38 | public void startNfc(Callback callback) { 39 | mNfcCallback = callback; 40 | Activity activity = getCurrentActivity(); 41 | Intent scanIntent = new Intent(activity,NfcCardReaderActivity.class); 42 | if (activity != null) { 43 | activity.startActivityForResult(scanIntent, INTENT); 44 | } 45 | 46 | } 47 | 48 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) { 49 | if(intent!=null && intent.hasExtra("cardNumber") && intent.hasExtra("expiryDate") && intent.hasExtra("cardType")){ 50 | if (requestCode==INTENT && activity.RESULT_OK==resultCode) { 51 | WritableMap NfcCardDetails = Arguments.createMap(); 52 | String cardNumber = intent.getExtras().getString("cardNumber"); 53 | String expiryDate = intent.getExtras().getString("expiryDate"); 54 | String cardType = intent.getExtras().getString("cardType"); 55 | NfcCardDetails.putString("cardNumber", cardNumber); 56 | NfcCardDetails.putString("expiryDate", expiryDate); 57 | NfcCardDetails.putString("cardType", cardType); 58 | mNfcCallback.invoke(NfcCardDetails); 59 | } 60 | } 61 | 62 | } 63 | 64 | public void onNewIntent(Intent intent) { 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-nfc-card-reader 2 | 3 | ## Description 4 | This package is specifically designed to scan credit/debit cards using an Android devices NFC reader and return the card number, expiry and card type. 5 | 6 | This package will only work on Android and isn't available for iOS as of 2020 because Apple do not allow 3rd party iPhone apps to use the Core NFC framework. 7 | ## Getting started 8 | 9 | `$ npm install react-native-nfc-card-reader --save` 10 | ### For Gradle 7.0 and < 7.2 you can do: 11 | `npm install "https://github.com/jackbayliss/react-native-nfc-card-reader.git#gradle7.0" --save` 12 | 13 | ### For Gradle 7.2 you can do: 14 | `npm install "https://github.com/jackbayliss/react-native-nfc-card-reader.git#gradle7.2" --save` 15 | 16 | ### Mostly automatic installation 17 | 18 | `$ react-native link react-native-nfc-card-reader` 19 | 20 | ## Usage 21 | ```javascript 22 | import NfcCardReader from 'react-native-nfc-card-reader'; 23 | 24 | NfcCardReader.startNfc(function(cardDetails){ 25 | // Card details contain the callback data below, see the options. 26 | that.setState({cardNumber : cardDetails.cardNumber}) 27 | that.setState({expiryDate : cardDetails.expiryDate}) 28 | that.setState({cardType : cardDetails.cardType}) 29 | }) 30 | ``` 31 | Ensure you add the following to your `AndroidManifest.xml` located in `android\app\src\main` 32 | ``` 33 | 34 | ``` 35 | ## Methods 36 | `startNfc(callback)` -> Will start the NFC Activity and expects a callback function that will return the card details it's scanned. 37 | 38 | ### Callback data 39 | `cardType` -> Provides the type of the card scanned. 40 | 41 | `cardNumber` -> Provides the full card number scanned. 42 | 43 | `expiryDate` -> Providers the expiry date of the card scanned. 44 | 45 | `firstName` -> Card owners first name. 46 | 47 | `lastName` -> Card owners last name. 48 | 49 | ### Example App 50 | You can find the sample app [here](https://github.com/jackbayliss/react-native-nfc-card-reader-sample) 51 | 52 | # Contributing 53 | I am by no means an Android developer, but saw this as a huge thing no one had produced for React Native. So, if you can expand or improve on my basic implementation feel free to create a pull request. 54 | 55 | 56 | # License 57 | MIT - expanded from [here](https://github.com/pro100svitlo/Credit-Card-NFC-Reader) 58 | 59 | 60 | A special thanks to [pro100svitlo](https://github.com/pro100svitlo/Credit-Card-NFC-Reader) for their library which gave me the ability to create this module. 61 | A special thanks to [decoder10](https://github.com/decoder10) for fixing a few issues, and adding firstname / lastname - as well as allowing any NFC cards to be scanned. 62 | 63 | 64 | -------------------------------------------------------------------------------- /android/src/main/java/com/jackbayliss/nfcreader/NfcCardReaderActivity.java: -------------------------------------------------------------------------------- 1 | package com.jackbayliss.nfcreader; 2 | 3 | import android.os.Bundle; 4 | import android.content.Intent; 5 | import android.nfc.NfcAdapter; 6 | import android.app.Activity; 7 | import android.widget.Toast; 8 | 9 | import com.pro100svitlo.creditCardNfcReader.CardNfcAsyncTask; 10 | import com.pro100svitlo.creditCardNfcReader.utils.CardNfcUtils; 11 | 12 | import com.facebook.react.ReactActivity; 13 | import com.facebook.react.ReactInstanceManager; 14 | import com.facebook.react.ReactNativeHost; 15 | import com.facebook.react.bridge.CatalystInstance; 16 | import com.facebook.react.bridge.ReactContext; 17 | import com.facebook.react.bridge.WritableNativeArray; 18 | import android.util.Log; 19 | public class NfcCardReaderActivity extends ReactActivity implements CardNfcAsyncTask.CardNfcInterface { 20 | 21 | private static final String TAG = "NfcCardReaderActivity"; 22 | private CardNfcAsyncTask mCardNfcAsyncTask; 23 | private CardNfcUtils mCardNfcUtils; 24 | private NfcAdapter mNfcAdapter; 25 | private boolean mIntentFromCreate; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 31 | setContentView(R.layout.nfcview); 32 | if (mNfcAdapter == null){ 33 | //do something if there are no nfc module on device 34 | } else { 35 | //do something if there are nfc module on device 36 | mCardNfcUtils = new CardNfcUtils(this); 37 | mIntentFromCreate = true; 38 | onNewIntent(getIntent()); 39 | } 40 | } 41 | 42 | @Override 43 | protected void onResume() { 44 | super.onResume(); 45 | mIntentFromCreate = false; 46 | if (mNfcAdapter != null && !mNfcAdapter.isEnabled()){ 47 | 48 | 49 | } else if (mNfcAdapter != null){ 50 | mCardNfcUtils.enableDispatch(); 51 | } 52 | } 53 | 54 | @Override 55 | public void onPause() { 56 | super.onPause(); 57 | if (mNfcAdapter != null) { 58 | mCardNfcUtils.disableDispatch(); 59 | } 60 | } 61 | @Override 62 | public void onNewIntent(Intent intent) { 63 | setIntent(intent); 64 | super.onNewIntent(intent); 65 | if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { 66 | 67 | mCardNfcAsyncTask = new CardNfcAsyncTask.Builder(this, intent, mIntentFromCreate) 68 | .build(); 69 | } 70 | 71 | } 72 | 73 | @Override 74 | public void finishNfcReadCard() { 75 | mCardNfcAsyncTask = null; 76 | } 77 | @Override 78 | public void cardWithLockedNfc() { 79 | Toast.makeText(this, "This card is locked!", Toast.LENGTH_SHORT).show(); 80 | } 81 | @Override 82 | public void unknownEmvCard() { 83 | Toast.makeText(this, "This is an unknown card!", Toast.LENGTH_SHORT).show(); 84 | } 85 | @Override 86 | public void doNotMoveCardSoFast() { 87 | Toast.makeText(this, "Don't move the card so quickly!", Toast.LENGTH_SHORT).show(); 88 | 89 | } 90 | 91 | @Override 92 | public void cardIsReadyToRead() { 93 | Toast.makeText(this, "Your card was read!", Toast.LENGTH_SHORT).show(); 94 | String card = mCardNfcAsyncTask.getCardNumber(); 95 | String expiryDate = mCardNfcAsyncTask.getCardExpireDate(); 96 | String cardType = mCardNfcAsyncTask.getCardType(); 97 | String firstName = mCardNfcAsyncTask.getHolderFirstname(); 98 | String lastName = mCardNfcAsyncTask.getHolderLastname(); 99 | 100 | Intent intent = new Intent(); 101 | intent.putExtra("cardNumber", card); 102 | intent.putExtra("expiryDate", expiryDate); 103 | intent.putExtra("cardType", cardType); 104 | intent.putExtra("firstName", firstName); 105 | intent.putExtra("lastName", lastName); 106 | 107 | setResult(RESULT_OK, intent); 108 | finish(); 109 | 110 | 111 | 112 | } 113 | 114 | @Override 115 | public void startNfcReadCard() { 116 | Toast.makeText(this, "NFC is scanning...", Toast.LENGTH_SHORT).show(); 117 | 118 | } 119 | 120 | 121 | 122 | 123 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // android/build.gradle 2 | 3 | // based on: 4 | // 5 | // * https://github.com/facebook/react-native/blob/0.60-stable/template/android/build.gradle 6 | // original location: 7 | // - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/build.gradle 8 | // 9 | // * https://github.com/facebook/react-native/blob/0.60-stable/template/android/app/build.gradle 10 | // original location: 11 | // - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/app/build.gradle 12 | 13 | def DEFAULT_COMPILE_SDK_VERSION = 28 14 | def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3' 15 | def DEFAULT_MIN_SDK_VERSION = 16 16 | def DEFAULT_TARGET_SDK_VERSION = 28 17 | 18 | def safeExtGet(prop, fallback) { 19 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 20 | } 21 | 22 | apply plugin: 'com.android.library' 23 | apply plugin: 'maven' 24 | 25 | buildscript { 26 | // The Android Gradle plugin is only required when opening the android folder stand-alone. 27 | // This avoids unnecessary downloads and potential conflicts when the library is included as a 28 | // module dependency in an application project. 29 | // ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies 30 | if (project == rootProject) { 31 | repositories { 32 | maven { url 'https://jitpack.io' } 33 | google() 34 | jcenter() 35 | } 36 | dependencies { 37 | classpath 'com.android.tools.build:gradle:3.4.1' 38 | } 39 | } 40 | } 41 | 42 | apply plugin: 'com.android.library' 43 | apply plugin: 'maven' 44 | 45 | android { 46 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION) 47 | buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION) 48 | defaultConfig { 49 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION) 50 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION) 51 | versionCode 1 52 | versionName "1.0" 53 | multiDexEnabled true 54 | } 55 | lintOptions { 56 | abortOnError false 57 | } 58 | } 59 | 60 | repositories { 61 | // ref: https://www.baeldung.com/maven-local-repository 62 | maven { url 'https://jitpack.io' } 63 | mavenLocal() 64 | maven { 65 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 66 | url "$rootDir/../node_modules/react-native/android" 67 | } 68 | maven { 69 | // Android JSC is installed from npm 70 | url "$rootDir/../node_modules/jsc-android/dist" 71 | } 72 | google() 73 | jcenter() 74 | } 75 | 76 | dependencies { 77 | //noinspection GradleDynamicVersion 78 | implementation 'com.facebook.react:react-native:+' // From node_modules 79 | } 80 | 81 | def configureReactNativePom(def pom) { 82 | def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text) 83 | 84 | pom.project { 85 | name packageJson.title 86 | artifactId packageJson.name 87 | version = packageJson.version 88 | group = "com.jackbayliss.nfcreader" 89 | description packageJson.description 90 | url packageJson.repository.baseUrl 91 | 92 | licenses { 93 | license { 94 | name packageJson.license 95 | url packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename 96 | distribution 'repo' 97 | } 98 | } 99 | 100 | developers { 101 | developer { 102 | id packageJson.author.username 103 | name packageJson.author.name 104 | } 105 | } 106 | } 107 | } 108 | 109 | afterEvaluate { project -> 110 | // some Gradle build hooks ref: 111 | // https://www.oreilly.com/library/view/gradle-beyond-the/9781449373801/ch03.html 112 | task androidJavadoc(type: Javadoc) { 113 | source = android.sourceSets.main.java.srcDirs 114 | classpath += files(android.bootClasspath) 115 | classpath += files(project.getConfigurations().getByName('compile').asList()) 116 | include '**/*.java' 117 | } 118 | 119 | task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) { 120 | classifier = 'javadoc' 121 | from androidJavadoc.destinationDir 122 | } 123 | 124 | task androidSourcesJar(type: Jar) { 125 | classifier = 'sources' 126 | from android.sourceSets.main.java.srcDirs 127 | include '**/*.java' 128 | } 129 | 130 | android.libraryVariants.all { variant -> 131 | def name = variant.name.capitalize() 132 | def javaCompileTask = variant.javaCompileProvider.get() 133 | 134 | task "jar${name}"(type: Jar, dependsOn: javaCompileTask) { 135 | from javaCompileTask.destinationDir 136 | } 137 | } 138 | 139 | artifacts { 140 | archives androidSourcesJar 141 | archives androidJavadocJar 142 | } 143 | 144 | task installArchives(type: Upload) { 145 | configuration = configurations.archives 146 | repositories.mavenDeployer { 147 | // Deploy to react-native-event-bridge/maven, ready to publish to npm 148 | repository url: "file://${projectDir}/../android/maven" 149 | configureReactNativePom pom 150 | } 151 | } 152 | } 153 | dependencies { 154 | implementation ('com.github.jackbayliss:Credit-Card-NFC-Reader:1.1.2'){ 155 | exclude module: 'bolts-android' 156 | 157 | } 158 | implementation 'com.android.support:multidex:1.0.0' 159 | } 160 | 161 | --------------------------------------------------------------------------------