├── .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 |
--------------------------------------------------------------------------------