Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


A mobile app for testing payment systems against double-spend attacks.

Double-spending is no longer a theoretical possibility but a practical reality. Most of the end-user applications used widely today leave their users vulnerable to being defrauded via double-spend attacks. PayNoWay is a tool that you can use to test the applications that you, or your business, depend on to accept on-chain cryptocurrency payments. If you would like to learn more about how double-spending works: Double-Spending Made Easy.

The PayNoWay app is available from the following sources:

Find more information about this project here:

The remaining topics consist of technical documentation. This information is useful if you would like to build the app from source or to fork and extend it:


  • This app is intended to be used for testing and educational purposes.
  • Please do not use this app to double-spend against merchants without their explicit consent.
  • A successful double-spend is not guaranteed - use at your own risk.
  • You are responsible for creating a backup of your private key(s). Without a backup, if you delete the app or lose your device, your funds will be permanently lost.

How to test a payment system

Follow these steps to test a payment system against a double-spend:

  • Open the send view in the app
  • Scan a QR code that contains either a payment request (e.g "bitcoin:ADDRESS?amount=0.00000123") or just an address
  • If just an address was scanned above, then enter a non-zero amount to send
  • Choose your fee rate (number of sats per vbyte)
  • Press the blue "Pay" button to send a payment transaction
  • Wait for the target merchant PoS to receive the unconfirmed transaction
  • Press the red "Return" button to send the double-spend transaction
  • Wait for one of the above transactions to confirm

Please consider adding the results of your double-spending tests to the Double-spending results repository.

How does a double-spend work?

This app uses the Replace-by-fee (RBF) feature of Bitcoin to replace (ie. double-spend) unconfirmed transactions. First, a payment transaction is sent with the target merchant's payment system as the recipient. Then a second, double-spend transaction is created and sent. This second transaction invalidates the first transaction by consuming at least one of the first transaction's inputs. The recipient of the double-spend transaction is your internal wallet address in the PayNoWay app. For more information about double-spending, please see Double-Spending Made Easy - the slides used during a presentation about this topic.


This section lists the software requirements needed to build the app from source.

  • nodejs - For Linux and Mac install node via nvm.
  • make
  • For Android development:
    • Java Development Kit (JDK) version 8 or higher. Use your system's native package manager to install the JDK (if available).
    • Android SDK - On Ubuntu 18.04 or later, it is possible to install Android Studio from Ubuntu Software Sources.
    • gradle
    • adb - Not required, but is recommended.

Getting Started

Before continuing, be sure you already have the project's requirements.

Download the project files via git:

git clone

Install the project's dependencies:

cd paynoway
npm ci

Build the application files:

npm run build


Before installing and running the app on Android, you must prepare the Android platform with cordova:

npm run prepare:android

This downloads the cordova plugins which are necessary to build the app for Android devices.

Running on Android (VM)

Run the following command to check to see if there are any available Android virtual devices:

adb devices

Install and run the app on the virtual device with the following command:

npm run android-vm

Running on Android (Device)

To install and run the app on an Android device, you must first:

Once developer mode and USB debugging are enabled, connect the device to your computer via USB. Run the following command to check to see if your computer is authorized:

adb devices

Install and run the app on the device: with the following command

npm run android

Create Signed APK

Create your signing key:

npm run android-generate-signing-key

Build a production APK:

npm run build:prod && npm run build:apk

If successful, it should have created a new .apk file at the following path:


To install the newly created APK onto an Android device:

adb install ./platforms/android/app/build/outputs/apk/release/app-release.apk
  • You may need to run adb devices before the above command.
  • And if the app is already installed on the device, you will need to use the -r flag to reinstall it.

Re-Generate Drawable Icons and Splash Images

The following script uses the SVG images found in ./assets/android to generate all sizes for icons and splash images for the Android platform:


Output files are written to ./src/images/android.

Prepare F-Droid Release

F-Droid requires the Android platform files (built by Cordova) in order to build an APK. This repository contains a special branch specifically for F-Droid - the branch contains the platform files from the latest, stable release.

After making a release of the app, the F-Droid branch must be updated as well. Run the following script to do this:

npm run release:fdroid

Note that write access for this repostiory is required.


This project includes automated tests. To run them:

npm test


Your help in translating the app into other languages is welcome. To do so please see the following steps:

  • Find the app's primary language file here.
  • Copy the contents of the file to the text editing program of your choice.
  • Change the values only - leave the keys un-translated. The reference keys are used internally in the app. Please see the following example:
var app = app || {};

app.lang = app.lang || {};

// The lang key here is the ISO_639-1 language code. See:
app.lang['es'] = (function() {

	return {
		'self.label': 'Español'
		'busy-text': 'Espere por favor...',
		'close': 'Cerrar',
		'copy': 'Copiar',

  • Once you've finished the translations, create an issue in this project here and copy/paste the contents of your translation file into the new issue.

    • Alternatively, you can fork this project and create a pull-request with the new file.
  • Thanks for taking the time to make a translation!




This project is licensed under the GNU General Public License v3 (GPL-3):

You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build & install instructions.