├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── GUIDE.md ├── LICENSE ├── README.md ├── RELEASE.md ├── android-sdk ├── .gitignore ├── android-sdk.iml ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bitpay │ │ └── sdk │ │ ├── android │ │ ├── BitPayAndroid.java │ │ ├── InvoiceActivity.java │ │ ├── interfaces │ │ │ ├── BitpayPromiseCallback.java │ │ │ ├── InvoiceCreationCallback.java │ │ │ ├── InvoicePromiseCallback.java │ │ │ ├── PromiseCallback.java │ │ │ └── RatesPromiseCallback.java │ │ └── promises │ │ │ └── BitpayPromise.java │ │ ├── controller │ │ ├── BitPay.java │ │ ├── BitPayException.java │ │ └── KeyUtils.java │ │ └── model │ │ ├── BuyerInfo.java │ │ ├── Invoice.java │ │ ├── InvoicePaymentUrls.java │ │ ├── InvoiceTransaction.java │ │ ├── Policy.java │ │ ├── Rate.java │ │ ├── Rates.java │ │ └── Token.java │ └── res │ ├── drawable-hdpi │ ├── accept_btc.png │ └── logo.png │ ├── drawable-mdpi │ ├── accept_btc.png │ └── logo.png │ ├── drawable-xhdpi │ ├── accept_btc.png │ └── logo.png │ ├── drawable-xxhdpi │ ├── accept_btc.png │ ├── logo.png │ ├── roundedbutton.xml │ └── roundedbutton_gray.xml │ ├── layout │ └── activity_invoice.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── bin └── getClientToken ├── bitpay-android-sdk.iml ├── build.gradle ├── dist └── android-sdk-1.0.1.aar ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sdk-android-bitpay.iml ├── settings.gradle └── test ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── bitpay │ └── sdk │ └── test │ └── BitPayAndroidTest.java └── main ├── AndroidManifest.xml └── res ├── drawable-hdpi └── ic_launcher.png ├── drawable-mdpi └── ic_launcher.png ├── drawable-xhdpi └── ic_launcher.png ├── drawable-xxhdpi └── ic_launcher.png └── values ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | *.iml 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Bitpay Android SDK -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.6 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GUIDE.md: -------------------------------------------------------------------------------- 1 | # Using the BitPay Android SDK 2 | 3 | ## Prerequisites 4 | 5 | You must have a BitPay merchant account to use this SDK. It's free to [sign-up for a BitPay merchant account](https://bitpay.com/start). 6 | 7 | Once you have a BitPay merchant account, you will need [a working BitPay Access Token](/api/getting-access.html) – this can be done either via the SDK (pairing process) or manually in [the BitPay Dashboard](https://bitpay.com/tokens). 8 | 9 | 10 | ## Installing 11 | 12 | You'll need to add this SDK as a dependency to your Android project. This process varies according to the build system you're using. 13 | 14 | ### Gradle 15 | 16 | If you're using Android Studio or gradle to build your app, add the following to your `build.gradle` file: 17 | 18 | ```gradle 19 | compile 'com.bitpay:android-sdk:1.0.0@aar' 20 | ``` 21 | 22 | Additionaly, you'll have to specify these dependencies: 23 | 24 | ```gradle 25 | compile 'com.google:bitcoinj:0.11.3' 26 | compile 'com.fasterxml.jackson.core:jackson-databind:2.4.2' 27 | compile 'com.fasterxml.jackson.core:jackson-annotations:2.2.3' 28 | compile 'org.apache.httpcomponents:httpclient-android:4.3.5' 29 | compile 'commons-codec:commons-codec:1.9' 30 | compile 'com.google.zxing:core:2.0' 31 | ``` 32 | 33 | Remember to add the maven central repository at the beginning of the file: 34 | 35 | ```java 36 | repositories { 37 | mavenCentral() 38 | } 39 | ``` 40 | 41 | ### Maven dependency 42 | 43 | If you're using maven, add the following dependency: 44 | 45 | ```xml 46 | 47 | com.bitpay 48 | android-sdk 49 | 1.0.0 50 | aar 51 | 52 | ``` 53 | 54 | ### AAR library 55 | 56 | A `.aar` library is provided in the `dist` folder, so you can include it in your Android project. 57 | 58 | ## Setup credentials 59 | 60 | ### 1. Create a BitPay Account 61 | 62 | Please go to https://bitpay.com to create an account. 63 | 64 | ### 2. Generate an Application Token 65 | 66 | Go to [*My Account* > *API Tokens*](https://bitpay.com/api-tokens) section. Click on the _Add New Token_ button and make a token with the `Point-of-Sale` capability for multiple clients. You can then include this token with your application. 67 | 68 | #### (Advanced) Pairing a 'Merchant' Token 69 | 70 | If you need access to all of the capabilities of the API, you'll need to create a token under the `Merchant` facade. Follow the same steps and pair your token with a command line tool located in the `bin` folder. 71 | 72 | ```bash 73 | $ cd bin 74 | $ npm install bitauth 75 | $ ./getClientToken 76 | Successfully paired. Your client token is: 70163c90f18df866d7a4ec3b8f7215f0013e3f81749f6222938a1f4d9ce3e97e 77 | ``` 78 | 79 | ## Sample Code and Usage 80 | 81 | ### Instantiating BitPayAndroid: 82 | 83 | ```java 84 | String clientToken = "Token from the previous section"; 85 | new BitPayAndroid.GetClientWithTokenTask() { 86 | @Override 87 | protected void onPostExecute(BitPayAndroid bitpay) { 88 | // ... 89 | } 90 | }.execute(clientToken); 91 | ``` 92 | 93 | This class inherits from `BitPay` so you can use all the methods of the [BitPay Java Client Library](https://github.com/bitpay/java-bitpay-client). 94 | 95 | #### Using promises: 96 | 97 | ```java 98 | String clientToken = "00000000000000000000000"; 99 | BitPayAndroid.withToken(clientToken).then(new BitpayPromiseCallback() { 100 | 101 | public void onSuccess(BitPayAndroid bitpay) { 102 | // ... 103 | } 104 | 105 | public void onError(BitPayException e) { 106 | // ... 107 | } 108 | }); 109 | ``` 110 | 111 | ### Use the testing server: 112 | 113 | ```java 114 | String clientToken = "00000000000000000000000"; 115 | BitPayAndroid.withToken(clientToken, "https://test.bitpay.com/").then( 116 | // ... 117 | ); 118 | ``` 119 | 120 | ### Creating a new invoice: 121 | 122 | ```java 123 | Invoice invoice = new Invoice(200, "USD"); 124 | new BitPayAndroid.CreateInvoiceTask(bitpay) { 125 | @Override 126 | protected void onPostExecute(Invoice invoice) { 127 | // ... 128 | } 129 | }.execute(invoice); 130 | ``` 131 | 132 | #### Using promises: 133 | 134 | ```java 135 | bitpay.createNewInvoice(new Invoice(200, "USD")).then(new InvoicePromiseCallback() { 136 | 137 | public void onSuccess(Invoice invoice) { 138 | // ... 139 | } 140 | public void onError(BitPayException e) { 141 | // ... 142 | } 143 | }); 144 | ``` 145 | 146 | ### The InvoiceActivity class 147 | 148 | This class helps your customer track the status of his payment, shows a QR code and a button to send him to his wallet, so he can pay. 149 | 150 | ```java 151 | Intent invoiceIntent = new Intent(this, InvoiceActivity.class); 152 | invoiceIntent.putExtra(InvoiceActivity.INVOICE_ID, invoice.getId()); 153 | startActivity(invoiceIntent); 154 | ``` 155 | 156 | ### Check if the user has a bitcoin wallet: 157 | 158 | ```java 159 | if (BitPayAndroid.isWalletAvailable(mContext)) { 160 | // ... 161 | } 162 | ``` 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 BitPay Inc. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/bitpay/android-sdk/master/LICENSE) 2 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.bitpay/android-sdk/badge.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/com.bitpay/android-sdk) 3 | 4 | # BitPay Android SDK 5 | 6 | This SDK allows your application to quickly create an invoice, show the user an option to pay you, and track the status of the payment. Accept bitcoins in your app with 10 lines of code! 7 | 8 | ## Quick Start Guide 9 | 10 | To get up and running with our Android SDK quickly, see the GUIDE here: https://github.com/bitpay/android-sdk/blob/master/GUIDE.md 11 | 12 | 13 | ## More Samples and Documentation 14 | 15 | ### Sample Project 16 | 17 | Take a look at [this project](https://github.com/bitpay/android-sdk-sample) where an integration with a mock application is shown. 18 | 19 | ### BitPay's API docs 20 | 21 | To read more about invoices refer to the BitPay's [API documentation](https://bitpay.com/api) 22 | 23 | 24 | ## Troubleshooting 25 | 26 | Contact support via [our official helpdesk](https://help.bitpay.com) or [ask the community](https://github.com/bitpay/android-sdk/issues). 27 | 28 | ## License 29 | 30 | Code released under [the MIT license](https://github.com/bitpay/android-sdk/blob/master/LICENSE). 31 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Releasing the Android SDK for distribution 2 | 3 | ## Prerequisites 4 | 5 | 1. You must have [Android Studio](http://developer.android.com/sdk/index.html) from Google. 6 | 7 | 2. You must clone this repo: 8 | 9 | ```bash 10 | $ git clone https://github.com/bitpay/android-sdk 11 | ``` 12 | 13 | 2. Check out the branch you wish to release to the world. At the time of this writing, we release from the master branch. 14 | 15 | ```bash 16 | $ git checkout master 17 | ``` 18 | 19 | 3. Open Android Studio and "create a new project" by using the sources that you cloned in the above steps. Once the project opens, depending on the state of Android Studio and the Gradle build system, you may be asked to update Gradle, sync build.gradle, etc. Please watch the "messages" window for explicit instructions. If you run into problems, open a Github issue on this project. 20 | 21 | 4. Build the project by going to the "Build" menu and choose "Build APK". This will generate build artifacts for the test project as well as a .aar file for the android-sdk.aar library. This is what we need to distribute. This aar file will most likely be located in "./android-sdk/build/outputs/aar/android-sdk-[debug|release].aar". If not, then: 22 | 23 | ```bash 24 | $ find . -name "*.aar" 25 | ``` 26 | 27 | 5. move this file to the dist directory and delete the last aar file from the last release. 28 | 29 | ```bash 30 | $ rm -fr dist/* && mv ./android-sdk/build/outputs/aar/android-sdk-release.aar dist/ 31 | ``` 32 | 33 | 6. Edit the android-sdk/build.gradle file. Change the value of the "version" key to have "-release" appended to it. Example: "version = 1.0.1-release" instead of "version = 1.0.1". This will the cryptographic signing when running gradle uploadArchive (we'll get to this later). 34 | 35 | 7. Commit the changes and push to the upstream master branch (bitpay/android-sdk). 36 | 37 | ```bash 38 | $ git add dist/ android-sdk/build.gradle && git commit -m"Adding artifacts for release." 39 | $ git push upstream master #your remote might be called origin 40 | ``` 41 | and tag the release sha 42 | 43 | ```bash 44 | $ git tag #e.g. git tag v1.0.1 45 | $ git push upstream 46 | ``` 47 | 48 | 8. If you don't have a GPG signing key, then proceed with steps 8 and 9. Ensure you have GnuPG (gnu privacy guard). 49 | 50 | ```bash 51 | $ gpg --version 52 | ``` 53 | 54 | Here is sample output from my machine: 55 | 56 | > gpg (GnuPG) 1.4.19
57 | > Copyright (C) 2015 Free Software Foundation, Inc.
58 | > License GPLv3+: GNU GPL version 3 or later [http://gnu.org/licenses/gpl.html](http://gnu.org/licenses/gpl.html)
59 | > This is free software: you are free to change and redistribute it.
60 | > There is NO WARRANTY, to the extent permitted by law.
61 | >
62 | > Home: ~/.gnupg
63 | > Supported algorithms:
64 | > Pubkey: RSA, RSA-E, RSA-S, ELG-E, DSA
65 | > Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
66 | > CAMELLIA128, CAMELLIA192, CAMELLIA256
67 | > Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
68 | > Compression: Uncompressed, ZIP, ZLIB, BZIP2
69 | 70 | Getting GnuPG for Mac: 71 | 72 | ```bash 73 | $ brew install gnupg 74 | ``` 75 | 76 | Note: there is no need to get GnuPG 2.x, Version 1 works fine. 77 | 78 | Getting GnuPG for Linux Unbuntu or Debian: 79 | 80 | ```bash 81 | $ sudo apt-get install gnupg 82 | ``` 83 | 84 | 9. Create a signing and encryption key (although only a signing key is required): 85 | 86 | ```bash 87 | $ gpg --gen-key 88 | ``` 89 | 90 | Just choose the defaults, RSA and RSA, 2048, does not expire, fill in your name, email, choose a password, please don't use an empty password. 91 | 92 | You should then have a signing key. 93 | 94 | Grab the keyID that you just created: 95 | 96 | ```bash 97 | $ gpg --list-keys 98 | ``` 99 | 100 | You should get output that looks similar to: 101 | 102 | > pub 2048R/EF6BDB7F 2015-03-23
103 | > uid Chris Kleeschulte
104 | > sub 2048R/CE81F194 2015-03-23
105 | 106 | The keyId in the example above is "EF6BDB7F" above. Yours will be different, but in the same position. 107 | 108 | ## Release all the things 109 | 110 | Background first. You will be doing three major steps. First, creating a properties file with credentials. Second, running a gradle "task" and checking the output for errors. Third, going to Sonatype's web interface to release the staged artifacts. 111 | 112 | 1. Edit "gradle.properties" located in the root of the android-sdk project. Add the following keys: 113 | 114 | > sonatypeUsername=bitpay
115 | > sonatypePassword=some password
116 | > signing.keyId=your key id
117 | > signing.password=your password
118 | > signing.secretKeyRingFile=/Users/yourusername/.gnupg/secring.gpg
119 | 120 | As for the "sonatypePassword", if you don't know it, [Reset it here](https://issues.sonatype.org/secure/ForgotLoginDetails.jspa). The username is bitpay. The password should come to "integrations@bitpay.com" so if you have access to this email box, then you can reset this. 121 | 122 | Note: NEVER commit your gradle.properties file! This could send sensitive information to a public place. 123 | 124 | 2. run the uploadArchives gradle task 125 | 126 | ```bash 127 | $ sh gradlew android-sdk:uploadArchives 128 | ``` 129 | 130 | the output should resemble this: 131 | 132 | > :android-sdk:preBuild UP-TO-DATE
133 | > :android-sdk:preReleaseBuild UP-TO-DATE
134 | > :android-sdk:compileReleaseNdk UP-TO-DATE
135 | > :android-sdk:compileLint
136 | > :android-sdk:copyReleaseLint UP-TO-DATE
137 | > :android-sdk:mergeReleaseProguardFiles UP-TO-DATE
138 | > :android-sdk:packageReleaseRenderscript UP-TO-DATE
139 | > :android-sdk:checkReleaseManifest
140 | > :android-sdk:prepareReleaseDependencies
141 | > :android-sdk:compileReleaseRenderscript UP-TO-DATE
142 | > :android-sdk:generateReleaseResValues UP-TO-DATE
143 | > :android-sdk:generateReleaseResources UP-TO-DATE
144 | > :android-sdk:packageReleaseResources UP-TO-DATE
145 | > :android-sdk:compileReleaseAidl UP-TO-DATE
146 | > :android-sdk:generateReleaseBuildConfig UP-TO-DATE
147 | > :android-sdk:generateReleaseAssets UP-TO-DATE
148 | > :android-sdk:mergeReleaseAssets UP-TO-DATE
149 | > :android-sdk:processReleaseManifest UP-TO-DATE
150 | > :android-sdk:processReleaseResources UP-TO-DATE
151 | > :android-sdk:generateReleaseSources UP-TO-DATE
152 | > :android-sdk:compileReleaseJavaWithJavac UP-TO-DATE
153 | > :android-sdk:processReleaseJavaRes UP-TO-DATE
154 | > :android-sdk:transformResourcesWithMergeJavaResForRelease UP-TO-DATE
155 | > :android-sdk:transformClassesAndResourcesWithSyncLibJarsForRelease UP-TO-DATE
156 | > :android-sdk:mergeReleaseJniLibFolders UP-TO-DATE
157 | > :android-sdk:transformNative_libsWithMergeJniLibsForRelease UP-TO-DATE
158 | > :android-sdk:transformNative_libsWithSyncJniLibsForRelease UP-TO-DATE
159 | > :android-sdk:bundleRelease UP-TO-DATE
160 | > :android-sdk:signArchives UP-TO-DATE
161 | > :android-sdk:uploadArchives
162 | > Could not find metadata com.bitpay:android-sdk/maven-metadata.xml in remote
(https://oss.sonatype.org/service/local/staging/deploy/maven2/)
163 | >
164 | > BUILD SUCCESSFUL
165 | >
166 | > Total time: 10.066 secs
167 | 168 | The key here is seeing: android-sdk:uploadArchives without errors. If all that goes ok, then proceed. If not, you may not have permission to login to oss.sonatype (400-level errors) or maybe your gpg key password is wrong or the path to the secret key is off. 169 | 170 | 3. Login to [sonatype](https://oss.sonatype.org/index.html#nexus-search;quick~bitpay). 171 | 172 | In the upper right corner there is a login link. Once logged in, click "Staging Repositories" under "Build Promotion", you should see a tab called "Staging Repositories" in the main window. Search the list for "combitpay". It may be the last one. It may be called "combitpay" with some other characters trailing after combitpay. 173 | 174 | Click the check box on combitpay. Look under the "content" tab below the list. Ensure the artifact is the one you expected. It should be something like "android-sdk--release". If so, click "Close" above the list in the "Staging Repositories". A text box will be presented for comments. No comments are needed, just close. 175 | 176 | This will go through some checks and if all is well, the "Release" button will be clickable. If so, click "Release", again a text box will be presented. No need for comments, just release. 177 | 178 | If the close operation fails, it could be that the artifact isn't signed properly. Check the output of gradle android-sdk:uploadArchives again. Ensure that :android-sdk:signArchives does not say "SKIPPED". If so, this is your problem. Check that you added "-release" to android-sdk/build.gradle version key. Click "Drop" on the Staging Repository and repeat the above steps. 179 | 180 | Rejoice. 181 | -------------------------------------------------------------------------------- /android-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-sdk/android-sdk.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /android-sdk/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android-library' 2 | apply plugin: 'maven' 3 | apply plugin: 'signing' 4 | 5 | version = "1.0.1" 6 | group = "com.bitpay" 7 | 8 | configurations { 9 | archives { 10 | extendsFrom configurations.default 11 | } 12 | } 13 | 14 | signing { 15 | required { has("release") && gradle.taskGraph.hasTask("uploadArchives") } 16 | sign configurations.archives 17 | } 18 | 19 | android { 20 | resourcePrefix 'bitpay_' 21 | compileSdkVersion 20 22 | buildToolsVersion "20.0.0" 23 | 24 | defaultConfig { 25 | minSdkVersion 15 26 | targetSdkVersion 19 27 | versionCode 2 28 | versionName "1.0.1" 29 | } 30 | buildTypes { 31 | release { 32 | } 33 | } 34 | packagingOptions { 35 | exclude 'META-INF/NOTICE' 36 | exclude 'META-INF/NOTICE.txt' 37 | exclude 'META-INF/LICENSE' 38 | exclude 'META-INF/LICENSE.txt' 39 | } 40 | } 41 | 42 | dependencies { 43 | compile fileTree(dir: 'libs', include: ['*.jar']) 44 | compile 'com.google:bitcoinj:0.11.3' 45 | compile 'com.fasterxml.jackson.core:jackson-databind:2.4.2' 46 | compile 'com.fasterxml.jackson.core:jackson-annotations:2.2.3' 47 | compile 'org.apache.httpcomponents:httpclient-android:4.3.5' 48 | compile 'commons-codec:commons-codec:1.9' 49 | compile 'com.google.zxing:core:2.0' 50 | } 51 | 52 | uploadArchives { 53 | configuration = configurations.archives 54 | repositories.mavenDeployer { 55 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 56 | 57 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 58 | authentication(userName: sonatypeUsername, password: sonatypePassword) 59 | } 60 | 61 | pom.project { 62 | name 'BitPay Android SDK' 63 | packaging 'aar' 64 | description 'An SDK to develop applications that use the BitPay API' 65 | url 'https://github.com/bitpay/android-sdk' 66 | 67 | scm { 68 | url 'scm:git@github.com:bitpay/android-sdk.git' 69 | connection 'scm:git@github.com:bitpay/android-sdk.git' 70 | developerConnection 'scm:git@github.com:bitpay/android-sdk.git' 71 | } 72 | 73 | licenses { 74 | license { 75 | name 'The MIT License' 76 | url 'http://www.opensource.org/licenses/MIT' 77 | distribution 'repo' 78 | } 79 | } 80 | 81 | developers { 82 | developer { 83 | id 'eordano' 84 | name 'Esteban Ordano' 85 | email 'eordano@gmail.com' 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /android-sdk/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /android-sdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/BitPayAndroid.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.net.Uri; 7 | import android.os.AsyncTask; 8 | import android.os.Parcel; 9 | import android.os.Parcelable; 10 | 11 | import com.bitpay.sdk.android.interfaces.PromiseCallback; 12 | import com.bitpay.sdk.android.promises.BitpayPromise; 13 | import com.bitpay.sdk.controller.BitPay; 14 | import com.bitpay.sdk.controller.BitPayException; 15 | import com.bitpay.sdk.controller.KeyUtils; 16 | import com.bitpay.sdk.model.Invoice; 17 | import com.bitpay.sdk.model.Rates; 18 | import com.bitpay.sdk.model.Token; 19 | import com.google.bitcoin.core.ECKey; 20 | 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | import java.util.concurrent.Executor; 28 | 29 | /** 30 | * Created by eordano on 9/10/14. 31 | */ 32 | public class BitPayAndroid extends BitPay implements Parcelable { 33 | 34 | private static final String EMPTY_BITCOIN_URI = "bitcoin:"; 35 | private Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; 36 | public static final List END_STATUS = Arrays.asList(Invoice.STATUS_COMPLETE, Invoice.STATUS_INVALID, Invoice.EXSTATUS_FALSE, Invoice.STATUS_CONFIRMED); 37 | 38 | public BitPayAndroid(ECKey ecKey, String clientName, String envUrl) throws BitPayException { 39 | super(ecKey, clientName, envUrl); 40 | } 41 | 42 | public BitPayAndroid(String clientName, String envUrl) { 43 | super(clientName, envUrl); 44 | } 45 | 46 | public BitPayAndroid(Token token, String envUrl) { 47 | super(token, envUrl); 48 | } 49 | public BitPayAndroid(String clientName) throws BitPayException { 50 | super(clientName); 51 | } 52 | public BitPayAndroid() { 53 | super(); 54 | } 55 | 56 | public void setExecutor(Executor executor) { 57 | this.executor = executor; 58 | } 59 | 60 | public static boolean isWalletAvailable(Context context) { 61 | 62 | final PackageManager packageManager = context.getPackageManager(); 63 | final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(EMPTY_BITCOIN_URI)); 64 | List resolveInfo = packageManager.queryIntentActivities(intent, 0); 65 | if (resolveInfo.size() > 0) { 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | public String getPrivateKey() { 72 | if (_ecKey == null) { 73 | return null; 74 | } 75 | return KeyUtils.exportPrivateKeyToHexa(_ecKey); 76 | } 77 | 78 | public static HashMap clients = new HashMap(); 79 | 80 | public BitpayPromise createNewInvoice(final Invoice builder) { 81 | return new BitpayPromise() { 82 | public void then(final PromiseCallback callback) { 83 | new CreateInvoiceTask(BitPayAndroid.this) { 84 | @Override 85 | protected void onPostExecute(Invoice invoice) { 86 | if (invoice == null) { 87 | callback.onError(error); 88 | } else { 89 | callback.onSuccess(invoice); 90 | } 91 | } 92 | }.executeOnExecutor(executor, builder); 93 | } 94 | }; 95 | } 96 | 97 | public BitpayPromise> getInvoicesAsync(final String from, final String to) { 98 | return new BitpayPromise>() { 99 | @Override 100 | public void then(final PromiseCallback> callback) { 101 | new AsyncTask>() { 102 | BitPayException error; 103 | @Override 104 | protected List doInBackground(String... strings) { 105 | try { 106 | return getInvoices(strings[0], strings[1]); 107 | } catch (BitPayException e) { 108 | error = e; 109 | return null; 110 | } 111 | } 112 | 113 | @Override 114 | protected void onPostExecute(List invoices) { 115 | if (error != null) { 116 | callback.onError(error); 117 | } else { 118 | callback.onSuccess(invoices); 119 | } 120 | } 121 | }.execute(from, to); 122 | } 123 | }; 124 | } 125 | 126 | public BitpayPromise getRatesAsync() { 127 | return new BitpayPromise() { 128 | @Override 129 | public void then(final PromiseCallback callback) { 130 | 131 | new AsyncTask() { 132 | protected BitPayException error; 133 | 134 | @Override 135 | protected final Rates doInBackground(Void... params) { 136 | try { 137 | return BitPayAndroid.this.getRates(); 138 | } catch (BitPayException e) { 139 | this.error = e; 140 | return null; 141 | } 142 | } 143 | 144 | @Override 145 | protected void onPostExecute(Rates invoice) { 146 | if (invoice == null) { 147 | callback.onError(error); 148 | } else { 149 | callback.onSuccess(invoice); 150 | } 151 | } 152 | }.executeOnExecutor(executor, null, null); 153 | } 154 | }; 155 | } 156 | 157 | public static BitpayPromise getClient(final String privateKey, final String server, final Executor executor) { 158 | return new BitpayPromise() { 159 | @Override 160 | public void then(final PromiseCallback callback) { 161 | new GetClientTask(){ 162 | @Override 163 | protected void onPostExecute(BitPayAndroid bitPayAndroid) { 164 | if (bitPayAndroid == null) { 165 | callback.onError(error); 166 | } else { 167 | callback.onSuccess(bitPayAndroid); 168 | } 169 | } 170 | }.executeOnExecutor(executor, privateKey, server); 171 | } 172 | }; 173 | } 174 | 175 | public static BitpayPromise getClient(final String privateKey) { 176 | return getClient(privateKey, AsyncTask.THREAD_POOL_EXECUTOR); 177 | } 178 | 179 | public static BitpayPromise getClient(final String privateKey, final Executor executor) { 180 | return getClient(privateKey, "https://bitpay.com/", executor); 181 | } 182 | public static BitpayPromise getClient(final String privateKey, final String server) { 183 | return getClient(privateKey, server, AsyncTask.THREAD_POOL_EXECUTOR); 184 | } 185 | 186 | public static BitpayPromise withToken(final String token, final String serverUrl) { 187 | return withToken(token, serverUrl, AsyncTask.THREAD_POOL_EXECUTOR); 188 | } 189 | 190 | public static BitpayPromise withToken(final String token) { 191 | return withToken(token, "https://bitpay.com/", AsyncTask.THREAD_POOL_EXECUTOR); 192 | } 193 | 194 | public static BitpayPromise withToken(final String token, final String serverUrl, final Executor executor) { 195 | return new BitpayPromise() { 196 | @Override 197 | public void then(final PromiseCallback callback) { 198 | new GetClientWithTokenTask(){ 199 | @Override 200 | protected void onPostExecute(BitPayAndroid bitPayAndroid) { 201 | if (bitPayAndroid == null) { 202 | callback.onError(error); 203 | } else { 204 | callback.onSuccess(bitPayAndroid); 205 | } 206 | } 207 | }.executeOnExecutor(executor, token, serverUrl); 208 | } 209 | }; 210 | } 211 | 212 | public BitpayPromise authorizeClientAsync(final String token) { 213 | return new BitpayPromise() { 214 | @Override 215 | public void then(final PromiseCallback callback) { 216 | new AsyncTask(){ 217 | 218 | private BitPayException error; 219 | @Override 220 | protected Void doInBackground(Void... voids) { 221 | try { 222 | authorizeClient(token); 223 | } catch (BitPayException e) { 224 | error = e; 225 | } 226 | return null; 227 | } 228 | 229 | @Override 230 | protected void onPostExecute(Void aVoid) { 231 | if (error != null) { 232 | callback.onError(error); 233 | } else { 234 | callback.onSuccess(null); 235 | } 236 | } 237 | }.execute(null, null); 238 | } 239 | }; 240 | } 241 | 242 | public BitpayPromise> createTokenAsync(final String facade) { 243 | return new BitpayPromise>() { 244 | @Override 245 | public void then(final PromiseCallback> callback) { 246 | new AsyncTask>(){ 247 | 248 | private BitPayException error; 249 | @Override 250 | protected List doInBackground(Void... voids) { 251 | try { 252 | return createToken(facade); 253 | } catch (BitPayException e) { 254 | error = e; 255 | } 256 | return null; 257 | } 258 | 259 | @Override 260 | protected void onPostExecute(List tokens) { 261 | if (error != null) { 262 | callback.onError(error); 263 | } else { 264 | callback.onSuccess(tokens); 265 | } 266 | } 267 | }.execute(null, null); 268 | } 269 | }; 270 | } 271 | 272 | public BitpayPromise> getTokensAsync() { 273 | return new BitpayPromise>() { 274 | @Override 275 | public void then(final PromiseCallback> callback) { 276 | new AsyncTask>(){ 277 | 278 | private BitPayException error; 279 | @Override 280 | protected List doInBackground(Void... voids) { 281 | try { 282 | getAccessTokens(); 283 | List result = new ArrayList(); 284 | for (Map.Entry entry : _tokenCache.entrySet()) { 285 | Token token = new Token(); 286 | token.setFacade(entry.getKey()); 287 | token.setValue(entry.getValue()); 288 | result.add(token); 289 | } 290 | return result; 291 | } catch (BitPayException e) { 292 | error = e; 293 | return null; 294 | } 295 | } 296 | 297 | @Override 298 | protected void onPostExecute(List tokens) { 299 | if (error != null) { 300 | callback.onError(error); 301 | } else { 302 | callback.onSuccess(tokens); 303 | } 304 | } 305 | }.execute(null, null); 306 | } 307 | }; 308 | } 309 | 310 | @Override 311 | public int describeContents() { 312 | return 0; 313 | } 314 | @Override 315 | public void writeToParcel(Parcel dest, int flags) { 316 | dest.writeString(_baseUrl); 317 | dest.writeString(_clientName); 318 | dest.writeSerializable(_tokenCache); 319 | dest.writeString(getPrivateKey()); 320 | } 321 | 322 | 323 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 324 | public BitPayAndroid createFromParcel(Parcel in) { 325 | String env = in.readString(); 326 | String client = in.readString(); 327 | BitPayAndroid result = new BitPayAndroid(client, env); 328 | 329 | result._tokenCache = (java.util.Hashtable) in.readSerializable(); 330 | String eckey = in.readString(); 331 | if (eckey != null) { 332 | try { 333 | result._ecKey = KeyUtils.loadEcKey(eckey); 334 | } catch (IOException e) { 335 | throw new RuntimeException(e); 336 | } 337 | } 338 | return result; 339 | } 340 | 341 | @Override 342 | public BitPayAndroid[] newArray(int size) { 343 | return new BitPayAndroid[size]; 344 | } 345 | }; 346 | 347 | public static class GetClientTask extends AsyncTask { 348 | protected BitPayException error; 349 | 350 | @Override 351 | protected final BitPayAndroid doInBackground(String... params) { 352 | 353 | try { 354 | String ecKey = params[0]; 355 | if (clients.containsKey(ecKey)) { 356 | return clients.get(ecKey); 357 | } 358 | BitPayAndroid client = new BitPayAndroid(KeyUtils.loadFromHexaEncodedPrivateKey(ecKey), "Android Client", "https://test.bitpay.com/"); 359 | clients.put(ecKey, client); 360 | return client; 361 | } catch (BitPayException e) { 362 | this.error = e; 363 | return null; 364 | } 365 | } 366 | } 367 | 368 | public static class GetClientWithTokenTask extends AsyncTask { 369 | protected BitPayException error; 370 | 371 | @Override 372 | protected final BitPayAndroid doInBackground(String... params) { 373 | String tokenStr = params[0]; 374 | Token token = new Token(); 375 | token.setFacade("pos"); 376 | token.setValue(tokenStr); 377 | String url = params.length > 1? params[1] : null; 378 | BitPayAndroid client = new BitPayAndroid(token, url == null ? "https://test.bitpay.com/" : url); 379 | return client; 380 | } 381 | } 382 | 383 | public static class CreateInvoiceTask extends AsyncTask { 384 | 385 | private BitPayAndroid mBitpay; 386 | protected BitPayException error; 387 | 388 | public CreateInvoiceTask (BitPayAndroid bitpay) { 389 | mBitpay = bitpay; 390 | } 391 | 392 | @Override 393 | protected final Invoice doInBackground(Invoice... params) { 394 | try { 395 | return mBitpay.createInvoice(params[0], "pos"); 396 | } catch (BitPayException e) { 397 | this.error = e; 398 | return null; 399 | } 400 | } 401 | } 402 | 403 | public static class FollowInvoiceStatusTask extends AsyncTask { 404 | 405 | private static final long DELAY_MS = 3000; 406 | private BitPayAndroid mBitpay; 407 | public FollowInvoiceStatusTask (BitPayAndroid bitpay) { 408 | mBitpay = bitpay; 409 | } 410 | 411 | @Override 412 | protected final Void doInBackground(String... params) { 413 | while (true) { 414 | try { 415 | try { 416 | Invoice invoice = mBitpay.getInvoice(params[0]); 417 | publishProgress(invoice); 418 | if (END_STATUS.contains(invoice.getStatus())) { 419 | return null; 420 | } 421 | } catch (BitPayException e) { 422 | } 423 | Thread.sleep(DELAY_MS); 424 | } catch (InterruptedException e) { 425 | return null; 426 | } 427 | } 428 | } 429 | 430 | @Override 431 | protected void onProgressUpdate(Invoice... values) { 432 | if (values[0] == null) { 433 | return; 434 | } 435 | String newState = values[0].getStatus(); 436 | if (newState.equals("paid")) { 437 | onStatePaid(); 438 | } 439 | if (newState.equals("confirmed")) { 440 | onStateConfirmed(); 441 | } 442 | if (newState.equals("complete")) { 443 | onStateComplete(); 444 | } 445 | if (newState.equals("expired")) { 446 | onStateExpired(); 447 | } 448 | if (newState.equals("invalid")) { 449 | onStateInvalid(); 450 | } 451 | } 452 | 453 | public void onStatePaid() { 454 | } 455 | public void onStateConfirmed() { 456 | } 457 | public void onStateComplete() { 458 | } 459 | public void onStateExpired() { 460 | } 461 | public void onStateInvalid() { 462 | } 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/InvoiceActivity.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android; 2 | 3 | import android.app.Activity; 4 | import android.content.ActivityNotFoundException; 5 | import android.content.ClipData; 6 | import android.content.ClipboardManager; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.graphics.Bitmap; 10 | import android.graphics.Color; 11 | import android.graphics.Paint; 12 | import android.graphics.Point; 13 | import android.net.Uri; 14 | import android.nfc.NdefMessage; 15 | import android.nfc.NdefRecord; 16 | import android.nfc.NfcAdapter; 17 | import android.nfc.NfcEvent; 18 | import android.os.AsyncTask; 19 | import android.os.Bundle; 20 | import android.util.Log; 21 | import android.view.Display; 22 | import android.view.View; 23 | import android.widget.Button; 24 | import android.widget.ImageView; 25 | import android.widget.ProgressBar; 26 | import android.widget.TextView; 27 | import android.widget.Toast; 28 | 29 | import com.bitpay.sdk.model.Invoice; 30 | import com.google.zxing.BarcodeFormat; 31 | import com.google.zxing.EncodeHintType; 32 | import com.google.zxing.MultiFormatWriter; 33 | import com.google.zxing.WriterException; 34 | import com.google.zxing.common.BitMatrix; 35 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; 36 | 37 | import java.io.File; 38 | import java.util.Date; 39 | import java.util.Hashtable; 40 | import java.util.concurrent.Executor; 41 | import java.util.concurrent.Executors; 42 | 43 | public class InvoiceActivity extends Activity implements NfcAdapter.CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback { 44 | 45 | public static final int RESULT_USER_CANCELED = 10; 46 | public static final int RESULT_STATE_INVALID = 11; 47 | public static final int RESULT_EXPIRED = 12; 48 | public static final int RESULT_COMPLETE = 13; 49 | public static final int RESULT_CONFIRMED = 14; 50 | public static final int RESULT_PAID = 15; 51 | public static final int RESULT_OVERPAID = 16; 52 | public static final int RESULT_PARTIALLY_PAID = 17; 53 | 54 | public static final Executor executor = Executors.newCachedThreadPool(); 55 | 56 | public static final String INVOICE = "invoice"; 57 | public static final String CLIENT = "bitpay"; 58 | private static final String TRIGGERED_WALLET = "triggered"; 59 | private static final long PAID_INTERVAL_MILLIS = 2000; 60 | 61 | private boolean triggeredWallet; 62 | 63 | private NfcAdapter mNfcAdapter; 64 | private Invoice mInvoice = null; 65 | private BitPayAndroid client; 66 | private AsyncTask followInvoiceTask; 67 | private AsyncTask updateTimerTask; 68 | 69 | private ProgressBar progressBar; 70 | private ProgressBar loadingQr; 71 | private TextView status; 72 | private TextView price; 73 | 74 | private Button launchWallet; 75 | private Button refund; 76 | private TextView showQR; 77 | private ImageView qrView; 78 | 79 | private TextView address; 80 | private TextView timeRemaining; 81 | private TextView conversion; 82 | 83 | public static int getResourseIdByName(String packageName, String className, String name) { 84 | Class r = null; 85 | int id = 0; 86 | try { 87 | r = Class.forName(packageName + ".R"); 88 | 89 | Class[] classes = r.getClasses(); 90 | Class desireClass = null; 91 | 92 | for (int i = 0; i < classes.length; i++) { 93 | if (classes[i].getName().split("\\$")[1].equals(className)) { 94 | desireClass = classes[i]; 95 | 96 | break; 97 | } 98 | } 99 | 100 | if (desireClass != null) 101 | id = desireClass.getField(name).getInt(desireClass); 102 | } catch (ClassNotFoundException e) { 103 | e.printStackTrace(); 104 | } catch (IllegalArgumentException e) { 105 | e.printStackTrace(); 106 | } catch (SecurityException e) { 107 | e.printStackTrace(); 108 | } catch (IllegalAccessException e) { 109 | e.printStackTrace(); 110 | } catch (NoSuchFieldException e) { 111 | e.printStackTrace(); 112 | } 113 | 114 | return id; 115 | 116 | } 117 | 118 | @Override 119 | protected void onSaveInstanceState(Bundle outState) { 120 | super.onSaveInstanceState(outState); 121 | outState.putParcelable(INVOICE, mInvoice); 122 | outState.putBoolean(TRIGGERED_WALLET, triggeredWallet); 123 | } 124 | 125 | @Override 126 | protected void onCreate(Bundle savedInstanceState) { 127 | super.onCreate(savedInstanceState); 128 | setResult(RESULT_USER_CANCELED); 129 | setContentView(getResourseIdByName(getPackageName(), "layout", "activity_invoice")); 130 | 131 | status = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "status")); 132 | price = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "price")); 133 | refund = (Button) findViewById(getResourseIdByName(getPackageName(), "id", "refund")); 134 | 135 | launchWallet = (Button) findViewById(getResourseIdByName(getPackageName(), "id", "launchWallet")); 136 | progressBar = (ProgressBar) findViewById(getResourseIdByName(getPackageName(), "id", "progressBar")); 137 | loadingQr = (ProgressBar) findViewById(getResourseIdByName(getPackageName(), "id", "loadingQr")); 138 | qrView = (ImageView) findViewById(getResourseIdByName(getPackageName(), "id", "qr")); 139 | 140 | showQR = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "showQr")); 141 | address = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "address")); 142 | timeRemaining = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "timeRemaining")); 143 | conversion = (TextView) findViewById(getResourseIdByName(getPackageName(), "id", "conversion")); 144 | 145 | if (savedInstanceState != null) { 146 | mInvoice = savedInstanceState.getParcelable(INVOICE); 147 | client = savedInstanceState.getParcelable(CLIENT); 148 | triggeredWallet = savedInstanceState.getBoolean(TRIGGERED_WALLET); 149 | } else { 150 | mInvoice = getIntent().getParcelableExtra(INVOICE); 151 | client = getIntent().getParcelableExtra(CLIENT); 152 | triggeredWallet = getIntent().getBooleanExtra(TRIGGERED_WALLET, false); 153 | } 154 | 155 | progressBar.setRotation(180); 156 | price.setText(mInvoice.getBtcPrice() + " BTC"); 157 | timeRemaining.setText(getRemainingTimeAsString()); 158 | conversion.setText(mInvoice.getBtcPrice() + " BTC = " + mInvoice.getPrice() + mInvoice.getCurrency()); 159 | address.setText(getAddress()); 160 | address.setPaintFlags(address.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); 161 | address.setOnClickListener(new View.OnClickListener() { 162 | @Override 163 | public void onClick(View v) { 164 | ClipboardManager ClipMan = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 165 | ClipMan.setPrimaryClip(ClipData.newPlainText("label", mInvoice.getPaymentUrls().getBIP73())); 166 | Toast toast = Toast.makeText(getApplicationContext(), "Copied payment address to clipboard", Toast.LENGTH_LONG); 167 | toast.show(); 168 | } 169 | }); 170 | showQR.setPaintFlags(showQR.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); 171 | showQR.setOnClickListener(new View.OnClickListener() { 172 | @Override 173 | public void onClick(View v) { 174 | triggerQrLoad(); 175 | } 176 | }); 177 | launchWallet.setOnClickListener(new View.OnClickListener() { 178 | @Override 179 | public void onClick(View v) { 180 | Intent bitcoinIntent = new Intent(Intent.ACTION_VIEW); 181 | bitcoinIntent.setData(Uri.parse(mInvoice.getBIP21due())); 182 | startActivity(bitcoinIntent); 183 | } 184 | }); 185 | 186 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 187 | if (mNfcAdapter != null) { 188 | // Register callback to set NDEF message 189 | mNfcAdapter.setNdefPushMessageCallback(this, this); 190 | 191 | // Register callback to listen for message-sent success 192 | mNfcAdapter.setOnNdefPushCompleteCallback(this, this); 193 | } else { 194 | Log.i("InvoiceActivity", "NFC is not available on this device"); 195 | } 196 | if (!triggeredWallet) { 197 | if (BitPayAndroid.isWalletAvailable(this)) { 198 | Intent bitcoinIntent = new Intent(Intent.ACTION_VIEW); 199 | bitcoinIntent.setData(Uri.parse(mInvoice.getBIP21due())); 200 | triggeredWallet = true; 201 | startActivity(bitcoinIntent); 202 | } else { 203 | Toast.makeText(getApplicationContext(), "You don't have any bitcoin wallets installed.", Toast.LENGTH_LONG).show(); 204 | triggerQrLoad(); 205 | } 206 | } else { 207 | triggerStatusCheck(); 208 | } 209 | } 210 | 211 | private void setupUpdateTimer() { 212 | updateTimerTask = new AsyncTask() { 213 | @Override 214 | protected Void doInBackground(Void... params) { 215 | while (true) { 216 | publishProgress(null, null); 217 | try { 218 | Thread.sleep(1000); 219 | } catch (InterruptedException e) { 220 | } 221 | } 222 | } 223 | 224 | @Override 225 | protected void onProgressUpdate(Void... values) { 226 | super.onProgressUpdate(values); 227 | 228 | int remainingSeconds = getRemainingSeconds(); 229 | if (remainingSeconds < 0) { 230 | timeRemaining.setText("-"); 231 | progressBar.setProgress(0); 232 | } else { 233 | timeRemaining.setText(getRemainingTimeAsString()); 234 | progressBar.setProgress((getRemainingSeconds() * 100) / (15 * 60)); 235 | } 236 | } 237 | }.executeOnExecutor(executor, null, null); 238 | } 239 | 240 | private void triggerStatusCheck() { 241 | if (followInvoiceTask != null) { 242 | followInvoiceTask.cancel(true); 243 | } 244 | if (client == null) { 245 | return; 246 | } 247 | followInvoiceTask = new BitPayAndroid.FollowInvoiceStatusTask(client) { 248 | @Override 249 | protected void onProgressUpdate(Invoice... values) { 250 | Invoice invoice = values[0]; 251 | Invoice prev = mInvoice; 252 | if (invoice != null) { 253 | mInvoice = invoice; 254 | } else { 255 | return; 256 | } 257 | if (invoice.getExceptionStatus().equals("paidPartial") && !prev.getBtcDue().equals(invoice.getBtcDue())) { 258 | status.setText("Partial payment received. Due amount:"); 259 | price.setText(invoice.getBtcDue() + " BTC"); 260 | InvoiceActivity.this.setResult(RESULT_PARTIALLY_PAID); 261 | if (qrView.getVisibility() == View.VISIBLE) { 262 | triggerQrLoad(); 263 | } 264 | return; 265 | } 266 | if (invoice.getExceptionStatus().equals("paidOver")) { 267 | InvoiceActivity.this.setResult(RESULT_OVERPAID); 268 | status.setText("This invoice was overpaid."); 269 | hidePaymentButtons(); 270 | showRefund(); 271 | this.cancel(true); 272 | return; 273 | } 274 | super.onProgressUpdate(values); 275 | } 276 | 277 | @Override 278 | public void onStatePaid() { 279 | InvoiceActivity.this.setResult(RESULT_PAID); 280 | checkExceptionAndFinish(); 281 | } 282 | 283 | private void checkExceptionAndFinish() { 284 | if (mInvoice.getExceptionStatus().equals("false")) { 285 | hidePaymentButtons(); 286 | showReceipt(); 287 | new AsyncTask() { 288 | 289 | @Override 290 | protected Void doInBackground(Void... params) { 291 | try { 292 | Thread.sleep(PAID_INTERVAL_MILLIS); 293 | } catch (InterruptedException e) { 294 | e.printStackTrace(); 295 | } 296 | return null; 297 | } 298 | 299 | @Override 300 | protected void onPostExecute(Void aVoid) { 301 | super.onPostExecute(aVoid); 302 | 303 | InvoiceActivity.this.finish(); 304 | } 305 | }.executeOnExecutor(executor, null, null); 306 | 307 | this.cancel(true); 308 | } 309 | } 310 | 311 | @Override 312 | public void onStateConfirmed() { 313 | InvoiceActivity.this.setResult(RESULT_CONFIRMED); 314 | checkExceptionAndFinish(); 315 | } 316 | 317 | @Override 318 | public void onStateComplete() { 319 | InvoiceActivity.this.setResult(RESULT_COMPLETE); 320 | checkExceptionAndFinish(); 321 | } 322 | 323 | @Override 324 | public void onStateExpired() { 325 | InvoiceActivity.this.setResult(RESULT_EXPIRED); 326 | checkExceptionAndFinish(); 327 | } 328 | 329 | @Override 330 | public void onStateInvalid() { 331 | InvoiceActivity.this.setResult(RESULT_STATE_INVALID); 332 | checkExceptionAndFinish(); 333 | } 334 | }.executeOnExecutor(executor, mInvoice.getId()); 335 | } 336 | 337 | private void showRefund() { 338 | refund = (Button) findViewById(getResourseIdByName(getPackageName(), "id", "refund")); 339 | refund.setVisibility(View.VISIBLE); 340 | refund.setOnClickListener(new View.OnClickListener() { 341 | @Override 342 | public void onClick(View v) { 343 | Intent intent = new Intent(Intent.ACTION_SEND); 344 | intent.setType("message/rfc822"); 345 | intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"support@bitpay.com"}); 346 | intent.putExtra(Intent.EXTRA_SUBJECT, "Refund Request"); 347 | intent.putExtra(Intent.EXTRA_TEXT, "Invoice: " + mInvoice.getUrl() + 348 | ((mInvoice.getRefundAddresses() != null && mInvoice.getRefundAddresses().size() > 0) 349 | ? "\nRefund Address:" + mInvoice.getRefundAddresses() : "") 350 | + "\nReason: Overpaid invoice"); 351 | 352 | startActivity(Intent.createChooser(intent, "Send Email")); 353 | } 354 | }); 355 | } 356 | 357 | private void showReceipt() { 358 | 359 | price.setVisibility(View.VISIBLE); 360 | price.setText(mInvoice.getBtcPrice() + " BTC paid"); 361 | status.setText("Your payment was received successfully"); 362 | } 363 | 364 | private void hidePaymentButtons() { 365 | 366 | progressBar.setVisibility(View.GONE); 367 | loadingQr.setVisibility(View.GONE); 368 | price.setVisibility(View.GONE); 369 | 370 | launchWallet.setVisibility(View.GONE); 371 | showQR.setVisibility(View.GONE); 372 | qrView.setVisibility(View.GONE); 373 | 374 | address.setVisibility(View.GONE); 375 | timeRemaining.setVisibility(View.GONE); 376 | conversion.setVisibility(View.GONE); 377 | } 378 | 379 | @Override 380 | protected void onPause() { 381 | super.onPause(); 382 | if (followInvoiceTask != null) { 383 | followInvoiceTask.cancel(true); 384 | } 385 | if (updateTimerTask != null) { 386 | updateTimerTask.cancel(true); 387 | } 388 | } 389 | 390 | @Override 391 | protected void onResume() { 392 | super.onResume(); 393 | triggerStatusCheck(); 394 | setupUpdateTimer(); 395 | } 396 | 397 | @Override 398 | public NdefMessage createNdefMessage(NfcEvent event) { 399 | 400 | if (mInvoice != null && mInvoice.getPaymentUrls() != null && mInvoice.getPaymentUrls().getBIP72b() != null) { 401 | return new NdefMessage(new NdefRecord[]{ 402 | NdefRecord.createUri(mInvoice.getPaymentUrls().getBIP72()) 403 | }); 404 | } 405 | return null; 406 | } 407 | 408 | @Override 409 | public void onNdefPushComplete(NfcEvent event) { 410 | // Pass 411 | } 412 | 413 | public int getRemainingSeconds() { 414 | Date now = new Date(); 415 | int millis = (int) (Math.abs(now.getTime() - Long.parseLong(mInvoice.getExpirationTime()))); 416 | return millis / 1000; 417 | } 418 | 419 | public String getRemainingTimeAsString() { 420 | int seconds = getRemainingSeconds(); 421 | return String.format("%02d:%02d", seconds / 60, seconds % 60); 422 | } 423 | 424 | public String getAddress() { 425 | String bip21 = mInvoice.getPaymentUrls().getBIP21(); 426 | return bip21.substring(bip21.indexOf(":") + 1, bip21.indexOf("?")); 427 | } 428 | 429 | public Bitmap generateQR(String text) { 430 | Display display = getWindowManager().getDefaultDisplay(); 431 | Point size = new Point(); 432 | display.getSize(size); 433 | int width = size.x * 3 / 4; 434 | int height = width; 435 | MultiFormatWriter writer = new MultiFormatWriter(); 436 | Hashtable hints = new Hashtable(); 437 | hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.Q); 438 | BitMatrix matrix = null; 439 | Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); 440 | try { 441 | matrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height, hints); 442 | } catch (WriterException e) { 443 | return bmp; 444 | } 445 | for (int x = 0; x < width; x++){ 446 | for (int y = 0; y < height; y++){ 447 | bmp.setPixel(x, y, matrix.get(x,y) ? Color.BLACK : Color.WHITE); 448 | } 449 | } 450 | return bmp; 451 | } 452 | 453 | private void triggerQrLoad() { 454 | new AsyncTask() { 455 | 456 | @Override 457 | protected void onPreExecute() { 458 | super.onPreExecute(); 459 | qrView.setVisibility(View.GONE); 460 | showQR.setVisibility(View.GONE); 461 | loadingQr.setVisibility(View.VISIBLE); 462 | } 463 | 464 | @Override 465 | protected Bitmap doInBackground(Void... params) { 466 | return generateQR(mInvoice.getBIP21due()); 467 | } 468 | 469 | @Override 470 | protected void onPostExecute(Bitmap bitmap) { 471 | super.onPostExecute(bitmap); 472 | qrView.setImageBitmap(bitmap); 473 | loadingQr.setVisibility(View.GONE); 474 | showQR.setVisibility(View.GONE); 475 | qrView.setVisibility(View.VISIBLE); 476 | } 477 | }.executeOnExecutor(executor, null, null); 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/interfaces/BitpayPromiseCallback.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.interfaces; 2 | 3 | import com.bitpay.sdk.android.BitPayAndroid; 4 | 5 | /** 6 | * Created by eordano on 9/11/14. 7 | */ 8 | public interface BitpayPromiseCallback extends PromiseCallback { 9 | } 10 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/interfaces/InvoiceCreationCallback.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.interfaces; 2 | 3 | import com.bitpay.sdk.controller.BitPayException; 4 | import com.bitpay.sdk.model.Invoice; 5 | 6 | /** 7 | * Created by eordano on 9/11/14. 8 | */ 9 | public interface InvoiceCreationCallback { 10 | public void onSuccess(Invoice invoice); 11 | public void onError(BitPayException exception); 12 | } 13 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/interfaces/InvoicePromiseCallback.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.interfaces; 2 | 3 | import com.bitpay.sdk.model.Invoice; 4 | 5 | /** 6 | * Created by eordano on 9/11/14. 7 | */ 8 | public interface InvoicePromiseCallback extends PromiseCallback { 9 | } 10 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/interfaces/PromiseCallback.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.interfaces; 2 | 3 | import com.bitpay.sdk.controller.BitPayException; 4 | 5 | /** 6 | * Created by eordano on 9/11/14. 7 | */ 8 | public interface PromiseCallback { 9 | public void onSuccess(D promised); 10 | public void onError(BitPayException e); 11 | } 12 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/interfaces/RatesPromiseCallback.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.interfaces; 2 | 3 | import com.bitpay.sdk.model.Rates; 4 | 5 | /** 6 | * Created by eordano on 9/11/14. 7 | */ 8 | public interface RatesPromiseCallback extends PromiseCallback { 9 | } 10 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/android/promises/BitpayPromise.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.android.promises; 2 | 3 | import com.bitpay.sdk.android.interfaces.PromiseCallback; 4 | 5 | /** 6 | * Created by eordano on 9/11/14. 7 | */ 8 | public abstract class BitpayPromise { 9 | public abstract void then(PromiseCallback callback); 10 | } 11 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/controller/BitPay.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.controller; 2 | 3 | import com.bitpay.sdk.model.Invoice; 4 | import com.bitpay.sdk.model.Rate; 5 | import com.bitpay.sdk.model.Rates; 6 | import com.bitpay.sdk.model.Token; 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import com.fasterxml.jackson.core.JsonProcessingException; 10 | import com.fasterxml.jackson.databind.JsonMappingException; 11 | import com.fasterxml.jackson.databind.JsonNode; 12 | import com.fasterxml.jackson.databind.ObjectMapper; 13 | import com.google.bitcoin.core.ECKey; 14 | 15 | import org.apache.http.HttpEntity; 16 | import org.apache.http.HttpResponse; 17 | import org.apache.http.ParseException; 18 | import org.apache.http.client.ClientProtocolException; 19 | import org.apache.http.client.HttpClient; 20 | import org.apache.http.client.methods.HttpGet; 21 | import org.apache.http.client.methods.HttpPost; 22 | import org.apache.http.entity.ByteArrayEntity; 23 | import org.apache.http.impl.client.DefaultHttpClient; 24 | import org.apache.http.util.EntityUtils; 25 | 26 | import java.io.IOException; 27 | import java.io.UnsupportedEncodingException; 28 | import java.net.URI; 29 | import java.net.URISyntaxException; 30 | import java.util.Arrays; 31 | import java.util.Date; 32 | import java.util.Hashtable; 33 | import java.util.List; 34 | 35 | @JsonIgnoreProperties(ignoreUnknown = true) 36 | public class BitPay { 37 | 38 | private static final String BITPAY_API_VERSION = "2.0.0"; 39 | private static final String BITPAY_PLUGIN_INFO = "BitPay Java Client " + BITPAY_API_VERSION; 40 | private static final String BITPAY_URL = "https://bitpay.com/"; 41 | 42 | public static final String FACADE_PAYROLL = "payroll"; 43 | public static final String FACADE_POS = "pos"; 44 | public static final String FACADE_MERCHANT = "merchant"; 45 | public static final String FACADE_USER = "user"; 46 | 47 | static private ObjectMapper mapper = new ObjectMapper(); 48 | 49 | protected static HttpClient _httpClient = new DefaultHttpClient(); 50 | protected String _baseUrl = BITPAY_URL; 51 | protected ECKey _ecKey = null; 52 | protected String _identity = ""; 53 | protected long _nonce = new Date().getTime(); 54 | protected boolean _disableNonce = false; 55 | protected String _clientName = ""; 56 | protected Hashtable _tokenCache; // {facade, token} 57 | 58 | /** 59 | * Constructor for use if the keys and SIN are managed by this library. 60 | * 61 | * @param clientName - The label for this client. 62 | * @param envUrl - The target server URL. 63 | * @throws BitPayException 64 | */ 65 | public BitPay(String clientName, String envUrl) { 66 | if (clientName.equals(BITPAY_PLUGIN_INFO)) { 67 | clientName += " on unknown host"; 68 | } 69 | _clientName = clientName; 70 | 71 | _baseUrl = envUrl; 72 | } 73 | 74 | public BitPay(String clientName) throws BitPayException { 75 | this(clientName, BITPAY_URL); 76 | } 77 | 78 | public BitPay(Token token, String url) { 79 | _clientName = "Android Authorized Client"; 80 | 81 | _baseUrl = url; 82 | 83 | _tokenCache = new Hashtable(); 84 | _tokenCache.put(token.getFacade(), token.getValue()); 85 | } 86 | 87 | public BitPay() { 88 | this(BITPAY_PLUGIN_INFO, BITPAY_URL); 89 | } 90 | 91 | /** 92 | * Constructor for use if the keys and SIN were derived external to this library. 93 | * 94 | * @param ecKey - An elliptical curve key. 95 | * @param clientName - The label for this client. 96 | * @param envUrl - The target server URL. 97 | * @throws BitPayException 98 | */ 99 | public BitPay(ECKey ecKey, String clientName, String envUrl) throws BitPayException { 100 | _ecKey = ecKey; 101 | this.deriveIdentity(); 102 | _baseUrl = envUrl; 103 | this.tryGetAccessTokens(); 104 | } 105 | 106 | public String getIdentity() { 107 | return _identity; 108 | } 109 | 110 | public boolean getDisableNonce() { 111 | return _disableNonce; 112 | } 113 | 114 | public void setDisableNonce(boolean value) { 115 | _disableNonce = value; 116 | } 117 | 118 | public void authorizeClient(String pairingCode) throws BitPayException { 119 | Token token = new Token(); 120 | token.setId(_identity); 121 | token.setGuid(this.getGuid()); 122 | token.setNonce(getNextNonce()); 123 | token.setPairingCode(pairingCode); 124 | token.setLabel(_clientName); 125 | 126 | ObjectMapper mapper = new ObjectMapper(); 127 | String json; 128 | try { 129 | json = mapper.writeValueAsString(token); 130 | } catch (JsonProcessingException e) { 131 | throw new BitPayException("Error - failed to serialize Token object : " + e.getMessage()); 132 | } 133 | HttpResponse response = this.post("tokens", json); 134 | 135 | List tokens; 136 | try { 137 | tokens = Arrays.asList(mapper.readValue(this.responseToJsonString(response), TokenWrapper.class).data); 138 | } catch (JsonProcessingException e) { 139 | throw new BitPayException("Error - failed to deserialize BitPay server response (Tokens) : " + e.getMessage()); 140 | } catch (IOException e) { 141 | throw new BitPayException("Error - failed to deserialize BitPay server response (Tokens) : " + e.getMessage()); 142 | } 143 | for (Token t : tokens) { 144 | _tokenCache.put(t.getFacade(), t.getValue()); 145 | } 146 | } 147 | 148 | 149 | public List createToken(String facade) throws BitPayException { 150 | Token token = new Token(); 151 | token.setId(_identity); 152 | token.setGuid(this.getGuid()); 153 | token.setNonce(getNextNonce()); 154 | token.setLabel(_clientName); 155 | token.setFacade(facade); 156 | 157 | String json; 158 | try { 159 | json = mapper.writeValueAsString(token); 160 | } catch (JsonProcessingException e) { 161 | throw new BitPayException("Error - failed to serialize Token object : " + e.getMessage()); 162 | } 163 | HttpResponse response = this.post("tokens", json); 164 | 165 | List tokens; 166 | try { 167 | tokens = Arrays.asList(mapper.readValue(this.responseToJsonString(response), TokenWrapper.class).data); 168 | } catch (JsonProcessingException e) { 169 | throw new BitPayException("Error - failed to deserialize BitPay server response (Tokens) : " + e.getMessage()); 170 | } catch (IOException e) { 171 | throw new BitPayException("Error - failed to deserialize BitPay server response (Tokens) : " + e.getMessage()); 172 | } 173 | for (Token t : tokens) { 174 | _tokenCache.put(t.getFacade(), t.getValue()); 175 | } 176 | return tokens; 177 | } 178 | 179 | public boolean clientIsAuthorized(String facade) { 180 | return _tokenCache.containsKey(facade); 181 | } 182 | 183 | public Invoice createInvoice(Invoice invoice, String facade) throws BitPayException { 184 | invoice.setGuid(this.getGuid()); 185 | 186 | String json; 187 | try { 188 | json = mapper.writeValueAsString(invoice); 189 | } catch (JsonProcessingException e) { 190 | throw new BitPayException("Error - failed to serialize Invoice object : " + e.getMessage()); 191 | } 192 | 193 | HttpResponse response = this.makeRPCpost(facade, "createInvoice", json); 194 | 195 | try { 196 | invoice = mapper.readValue(this.responseToJsonString(response), InvoiceWrapper.class).data; 197 | } catch (JsonProcessingException e) { 198 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 199 | } catch (IOException e) { 200 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 201 | } 202 | return invoice; 203 | } 204 | 205 | public Invoice createInvoice(Invoice invoice) throws BitPayException { 206 | return this.createInvoice(invoice, FACADE_POS); 207 | } 208 | 209 | public Invoice getInvoice(String invoiceId) throws BitPayException { 210 | HttpResponse response = this.get("invoices/" + invoiceId); 211 | Invoice i; 212 | try { 213 | i = mapper.readValue(this.responseToJsonString(response), InvoiceWrapper.class).data; 214 | } catch (JsonProcessingException e) { 215 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 216 | } catch (IOException e) { 217 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 218 | } 219 | return i; 220 | } 221 | 222 | public List getInvoices(String dateStart, String dateEnd) throws BitPayException { 223 | Hashtable parameters = this.getParams(); 224 | parameters.put("token", this.getAccessToken(FACADE_MERCHANT)); 225 | parameters.put("dateStart", dateStart); 226 | parameters.put("dateEnd", dateEnd); 227 | HttpResponse response = this.get("invoices", parameters); 228 | 229 | List invoices; 230 | try { 231 | invoices = mapper.readValue(this.responseToJsonString(response), InvoicesWrapper.class).data; 232 | } catch (JsonProcessingException e) { 233 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoices) : " + e.getMessage()); 234 | } catch (IOException e) { 235 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoices) : " + e.getMessage()); 236 | } 237 | return invoices; 238 | } 239 | 240 | public Rates getRates() throws BitPayException { 241 | HttpResponse response = this.get("rates"); 242 | 243 | List rates; 244 | try { 245 | rates = Arrays.asList(mapper.readValue(this.responseToJsonString(response), RatesWrapper.class).data); 246 | } catch (JsonProcessingException e) { 247 | throw new BitPayException("Error - failed to deserialize BitPay server response (Rates) : " + e.getMessage()); 248 | } catch (IOException e) { 249 | throw new BitPayException("Error - failed to deserialize BitPay server response (Rates) : " + e.getMessage()); 250 | } 251 | return new Rates(rates); 252 | } 253 | 254 | private void initKeys() { 255 | _ecKey = KeyUtils.createEcKey(); 256 | } 257 | 258 | private void deriveIdentity() throws IllegalArgumentException { 259 | // Identity in this implementation is defined to be the SIN. 260 | _identity = KeyUtils.deriveSIN(_ecKey); 261 | } 262 | 263 | private long getNextNonce() { 264 | if (!getDisableNonce()) { 265 | _nonce++; 266 | } else { 267 | _nonce = 0; // Nonce must be 0 when it has been disabled (0 value prevents serialization) 268 | } 269 | return _nonce; 270 | } 271 | 272 | private Hashtable responseToTokenCache(HttpResponse response) throws BitPayException { 273 | // The response is expected to be an array of key/value pairs (facade name = token). 274 | String json = this.responseToJsonString(response); 275 | 276 | _tokenCache = new Hashtable(); 277 | try { 278 | for (Hashtable entry1 : mapper.readValue(json, TokenCacheWrapper.class).data) { 279 | _tokenCache.putAll(entry1); 280 | } 281 | } catch (JsonProcessingException e) { 282 | throw new BitPayException("Error - failed to deserialize BitPay server response (Token array) : " + e.getMessage()); 283 | } catch (IOException e) { 284 | throw new BitPayException("Error - failed to deserialize BitPay server response (Token array) : " + e.getMessage()); 285 | } 286 | return _tokenCache; 287 | } 288 | 289 | private void clearAccessTokenCache() { 290 | _tokenCache = new Hashtable(); 291 | } 292 | 293 | private boolean tryGetAccessTokens() throws BitPayException { 294 | // Attempt to get access tokens for this client identity. 295 | try { 296 | // Success if at least one access token was returned. 297 | return this.getAccessTokens() > 0; 298 | } catch (BitPayException ex) { 299 | // If the error states that the identity is invalid then this client has not been 300 | // registered with the BitPay account. 301 | if (ex.getMessage().contains("Unauthorized sin")) { 302 | this.clearAccessTokenCache(); 303 | return false; 304 | } else { 305 | // Propagate all other errors. 306 | throw ex; 307 | } 308 | } 309 | } 310 | 311 | public List getTokens() throws BitPayException { 312 | HttpResponse response = this.get("tokens", getParams()); 313 | try { 314 | return Arrays.asList(mapper.readValue(this.responseToJsonString(response), TokenWrapper.class).data); 315 | } catch (JsonProcessingException e) { 316 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 317 | } catch (IOException e) { 318 | throw new BitPayException("Error - failed to deserialize BitPay server response (Invoice) : " + e.getMessage()); 319 | } 320 | } 321 | 322 | protected int getAccessTokens() throws BitPayException { 323 | this.clearAccessTokenCache(); 324 | Hashtable parameters = this.getParams(); 325 | HttpResponse response = this.get("tokens", parameters); 326 | _tokenCache = responseToTokenCache(response); 327 | return _tokenCache.size(); 328 | } 329 | 330 | private String getAccessToken(String facade) throws BitPayException { 331 | if (!_tokenCache.containsKey(facade)) { 332 | throw new BitPayException("Error: You do not have access to facade: " + facade); 333 | } 334 | return _tokenCache.get(facade); 335 | } 336 | 337 | private Hashtable getParams() { 338 | Hashtable params = new Hashtable(); 339 | params.put("nonce", getNextNonce() + ""); 340 | return params; 341 | } 342 | 343 | private HttpResponse get(String uri, Hashtable parameters) throws BitPayException { 344 | try { 345 | 346 | String fullURL = _baseUrl + uri; 347 | HttpGet get = new HttpGet(fullURL); 348 | if (parameters != null) { 349 | fullURL += "?"; 350 | for (String key : parameters.keySet()) { 351 | fullURL += key + "=" + parameters.get(key) + "&"; 352 | } 353 | fullURL = fullURL.substring(0, fullURL.length() - 1); 354 | get.setURI(new URI(fullURL)); 355 | String signature = KeyUtils.sign(_ecKey, fullURL); 356 | get.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); 357 | get.addHeader("x-accept-version", BITPAY_API_VERSION); 358 | get.addHeader("x-signature", signature); 359 | get.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); 360 | } 361 | return _httpClient.execute(get); 362 | 363 | } catch (URISyntaxException e) { 364 | throw new BitPayException("Error: GET failed\n" + e.getMessage()); 365 | } catch (ClientProtocolException e) { 366 | throw new BitPayException("Error: GET failed\n" + e.getMessage()); 367 | } catch (IOException e) { 368 | throw new BitPayException("Error: GET failed\n" + e.getMessage()); 369 | } 370 | } 371 | 372 | private HttpResponse get(String uri) throws BitPayException { 373 | return this.get(uri, null); 374 | } 375 | 376 | private HttpResponse post(String uri, String json, boolean signatureRequired) throws BitPayException { 377 | try { 378 | HttpPost post = new HttpPost(_baseUrl + uri); 379 | post.setEntity(new ByteArrayEntity(json.toString().getBytes("UTF8"))); 380 | if (signatureRequired) { 381 | String signature = KeyUtils.sign(_ecKey, _baseUrl + uri + json); 382 | post.addHeader("x-signature", signature); 383 | post.addHeader("x-identity", KeyUtils.bytesToHex(_ecKey.getPubKey())); 384 | } 385 | post.addHeader("x-accept-version", BITPAY_API_VERSION); 386 | post.addHeader("x-bitpay-plugin-info", BITPAY_PLUGIN_INFO); 387 | post.addHeader("Content-Type", "application/json"); 388 | return _httpClient.execute(post); 389 | 390 | } catch (UnsupportedEncodingException e) { 391 | throw new BitPayException("Error: POST failed\n" + e.getMessage()); 392 | } catch (ClientProtocolException e) { 393 | throw new BitPayException("Error: POST failed\n" + e.getMessage()); 394 | } catch (IOException e) { 395 | throw new BitPayException("Error: POST failed\n" + e.getMessage()); 396 | } 397 | } 398 | 399 | private HttpResponse post(String uri, String json) throws BitPayException { 400 | return this.post(uri, json, false); 401 | } 402 | 403 | private HttpResponse postWithSignature(String uri, String json) throws BitPayException { 404 | return this.post(uri, json, true); 405 | } 406 | 407 | private HttpResponse makeRPCpost(String facade, String method, String params) throws BitPayException { 408 | try { 409 | return post("api/" + _tokenCache.get(facade), new ObjectMapper().writeValueAsString(new RPCCall(method, params))); 410 | } catch (JsonProcessingException e) { 411 | throw new BitPayException("Unable to parse JSON", e); 412 | } 413 | } 414 | 415 | private class RPCCall { 416 | @JsonProperty 417 | private final String method; 418 | @JsonProperty 419 | private final String params; 420 | 421 | RPCCall(String method, String params) { 422 | this.method = method; 423 | this.params = params; 424 | } 425 | } 426 | 427 | private String responseToJsonString(HttpResponse response) throws BitPayException { 428 | if (response == null) { 429 | throw new BitPayException("Error: HTTP response is null"); 430 | } 431 | try { 432 | // Get the JSON string from the response. 433 | HttpEntity entity = response.getEntity(); 434 | String jsonString; 435 | jsonString = EntityUtils.toString(entity, "UTF-8"); 436 | 437 | ObjectMapper mapper = new ObjectMapper(); 438 | JsonNode rootNode = mapper.readTree(jsonString); 439 | 440 | JsonNode node = rootNode.get("error"); 441 | if (node != null) { 442 | throw new BitPayException("Error: " + jsonString); 443 | } 444 | 445 | node = rootNode.get("errors"); 446 | if (node != null) { 447 | String message = "Multiple errors:"; 448 | if (node.isArray()) { 449 | for (final JsonNode errorNode : node) { 450 | message += "\n" + errorNode.asText(); 451 | } 452 | throw new BitPayException(message); 453 | } 454 | } 455 | 456 | return jsonString; 457 | 458 | } catch (ParseException e) { 459 | throw new BitPayException("Error - failed to retrieve HTTP response body : " + e.getMessage()); 460 | } catch (JsonMappingException e) { 461 | throw new BitPayException("Error - failed to parse json response to map : " + e.getMessage()); 462 | } catch (IOException e) { 463 | throw new BitPayException("Error - failed to retrieve HTTP response body : " + e.getMessage()); 464 | } 465 | } 466 | 467 | private String getGuid() { 468 | int Min = 0; 469 | int Max = 99999999; 470 | return Min + (int) (Math.random() * ((Max - Min) + 1)) + ""; 471 | } 472 | 473 | public Token newToken(String facade) { 474 | Token token = new Token(); 475 | token.setId(_identity); 476 | token.setGuid(this.getGuid()); 477 | token.setNonce(getNextNonce()); 478 | token.setLabel(_clientName); 479 | token.setFacade(facade); 480 | return token; 481 | } 482 | 483 | public static class Wrapper { 484 | public Wrapper() { 485 | } 486 | 487 | @JsonProperty("data") 488 | public D data; 489 | @JsonProperty("facade") 490 | public String facade; 491 | } 492 | 493 | public static class TokenWrapper extends Wrapper { 494 | public TokenWrapper() { 495 | super(); 496 | } 497 | } 498 | 499 | public static class InvoiceWrapper extends Wrapper { 500 | public InvoiceWrapper() { 501 | super(); 502 | } 503 | } 504 | 505 | public static class InvoicesWrapper extends Wrapper> { 506 | public InvoicesWrapper() { 507 | super(); 508 | } 509 | } 510 | 511 | public static class TokenCacheWrapper extends Wrapper[]> { 512 | public TokenCacheWrapper() { 513 | super(); 514 | } 515 | } 516 | 517 | public static class RatesWrapper extends Wrapper { 518 | public RatesWrapper() { 519 | super(); 520 | } 521 | } 522 | } 523 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/controller/BitPayException.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.controller; 2 | 3 | public class BitPayException extends Exception { 4 | 5 | public BitPayException(String message) { 6 | super(message); 7 | } 8 | 9 | private static final long serialVersionUID = 1L; 10 | 11 | public BitPayException(String s, Exception e) { 12 | super(s, e); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/controller/KeyUtils.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.controller; 2 | 3 | import com.google.bitcoin.core.Base58; 4 | import com.google.bitcoin.core.ECKey; 5 | import com.google.bitcoin.core.ECKey.ECDSASignature; 6 | import com.google.bitcoin.core.Sha256Hash; 7 | import com.google.bitcoin.core.Utils; 8 | 9 | import java.io.IOException; 10 | import java.math.BigInteger; 11 | 12 | public class KeyUtils { 13 | 14 | final private static char[] hexArray = "0123456789abcdef".toCharArray(); 15 | 16 | public KeyUtils() {} 17 | 18 | public static ECKey createEcKey() 19 | { 20 | //Default constructor uses SecureRandom numbers. 21 | return new ECKey(); 22 | } 23 | 24 | public static ECKey loadEcKey(String string) throws IOException 25 | { 26 | ECKey key = ECKey.fromASN1(hexToBytes(string)); 27 | return key; 28 | } 29 | 30 | public static ECKey loadFromHexaEncodedPrivateKey(String key) { 31 | return new ECKey(new BigInteger(key, 16)); 32 | } 33 | public static String exportPrivateKeyToHexa(ECKey key) { 34 | return bytesToHex(key.getPrivKeyBytes()); 35 | } 36 | 37 | public static String exportEcKey(ECKey ecKey) throws IOException { 38 | return bytesToHex(ecKey.toASN1()); 39 | } 40 | 41 | public static String deriveSIN(ECKey ecKey) throws IllegalArgumentException 42 | { 43 | // Get sha256 hash and then the RIPEMD-160 hash of the public key (this call gets the result in one step). 44 | byte[] pubKeyHash = ecKey.getPubKeyHash(); 45 | 46 | // Convert binary pubKeyHash, SINtype and version to Hex 47 | String version = "0F"; 48 | String SINtype = "02"; 49 | String pubKeyHashHex = bytesToHex(pubKeyHash); 50 | 51 | // Concatenate all three elements 52 | String preSIN = version + SINtype + pubKeyHashHex; 53 | 54 | // Convert the hex string back to binary and double sha256 hash it leaving in binary both times 55 | byte[] preSINbyte = hexToBytes(preSIN); 56 | byte[] hash2Bytes = Utils.doubleDigest(preSINbyte); 57 | 58 | // Convert back to hex and take first four bytes 59 | String hashString = bytesToHex(hash2Bytes); 60 | String first4Bytes = hashString.substring(0, 8); 61 | 62 | // Append first four bytes to fully appended SIN string 63 | String unencoded = preSIN + first4Bytes; 64 | byte[] unencodedBytes = new BigInteger(unencoded, 16).toByteArray(); 65 | String encoded = Base58.encode(unencodedBytes); 66 | 67 | return encoded; 68 | } 69 | 70 | public static String sign(ECKey key, String input) { 71 | byte[] data = input.getBytes(); 72 | Sha256Hash hash = Sha256Hash.create(data); 73 | ECDSASignature sig = key.sign(hash, null); 74 | byte[] bytes = sig.encodeToDER(); 75 | return bytesToHex(bytes); 76 | } 77 | 78 | private static int getHexVal(char hex) 79 | { 80 | int val = (int)hex; 81 | return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); 82 | } 83 | 84 | public static byte[] hexToBytes(String hex) throws IllegalArgumentException 85 | { 86 | char[] hexArray = hex.toCharArray(); 87 | 88 | if (hex.length() % 2 == 1) 89 | { 90 | throw new IllegalArgumentException("Error: The binary key cannot have an odd number of digits"); 91 | } 92 | byte[] arr = new byte[hex.length() >> 1]; 93 | 94 | for (int i = 0; i < hex.length() >> 1; ++i) 95 | { 96 | arr[i] = (byte)((getHexVal(hexArray[i << 1]) << 4) + (getHexVal(hexArray[(i << 1) + 1]))); 97 | } 98 | return arr; 99 | } 100 | 101 | public static String bytesToHex(byte[] bytes) { 102 | char[] hexChars = new char[bytes.length * 2]; 103 | for ( int j = 0; j < bytes.length; j++ ) { 104 | int v = bytes[j] & 0xFF; 105 | hexChars[j * 2] = hexArray[v >>> 4]; 106 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 107 | } 108 | return new String(hexChars); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/BuyerInfo.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | 9 | /** 10 | * Created by eordano on 10/6/14. 11 | */ 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) 14 | public class BuyerInfo implements Parcelable { 15 | private String name; 16 | private String address1; 17 | private String address2; 18 | private String locality; 19 | private String region; 20 | private String postalCode; 21 | private String email; 22 | private String phone; 23 | 24 | public String getPhone() { 25 | return phone; 26 | } 27 | 28 | public void setPhone(String phone) { 29 | this.phone = phone; 30 | } 31 | 32 | public String getEmail() { 33 | return email; 34 | } 35 | 36 | public void setEmail(String email) { 37 | this.email = email; 38 | } 39 | 40 | public String getPostalCode() { 41 | return postalCode; 42 | } 43 | 44 | public void setPostalCode(String postalCode) { 45 | this.postalCode = postalCode; 46 | } 47 | 48 | public String getRegion() { 49 | return region; 50 | } 51 | 52 | public void setRegion(String region) { 53 | this.region = region; 54 | } 55 | 56 | public String getLocality() { 57 | return locality; 58 | } 59 | 60 | public void setLocality(String locality) { 61 | this.locality = locality; 62 | } 63 | 64 | public String getAddress2() { 65 | return address2; 66 | } 67 | 68 | public void setAddress2(String address2) { 69 | this.address2 = address2; 70 | } 71 | 72 | public String getAddress1() { 73 | return address1; 74 | } 75 | 76 | public void setAddress1(String address1) { 77 | this.address1 = address1; 78 | } 79 | 80 | public String getName() { 81 | return name; 82 | } 83 | 84 | public void setName(String name) { 85 | this.name = name; 86 | } 87 | 88 | public BuyerInfo() { 89 | } 90 | 91 | @Override 92 | public int describeContents() { 93 | return 0; 94 | } 95 | 96 | @Override 97 | public void writeToParcel(Parcel dest, int flags) { 98 | dest.writeString(name); 99 | dest.writeString(address1); 100 | dest.writeString(address2); 101 | dest.writeString(locality); 102 | dest.writeString(region); 103 | dest.writeString(postalCode); 104 | dest.writeString(email); 105 | dest.writeString(phone); 106 | 107 | } 108 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 109 | public BuyerInfo createFromParcel(Parcel in) { 110 | 111 | BuyerInfo buyerInfo = new BuyerInfo(); 112 | buyerInfo.name = in.readString(); 113 | buyerInfo.address1 = in.readString(); 114 | buyerInfo.address2 = in.readString(); 115 | buyerInfo.locality = in.readString(); 116 | buyerInfo.region = in.readString(); 117 | buyerInfo.postalCode = in.readString(); 118 | buyerInfo.email = in.readString(); 119 | buyerInfo.phone = in.readString(); 120 | return buyerInfo; 121 | } 122 | 123 | @Override 124 | public BuyerInfo[] newArray(int size) { 125 | return new BuyerInfo[size]; 126 | } 127 | }; 128 | } 129 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.bitpay.sdk.controller.BitPayException; 7 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 8 | import com.fasterxml.jackson.annotation.JsonIgnore; 9 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 10 | import com.fasterxml.jackson.annotation.JsonInclude; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | import com.fasterxml.jackson.databind.DeserializationConfig; 13 | import com.fasterxml.jackson.databind.ObjectMapper; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Hashtable; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | * Model for an Invoice object. 22 | * 23 | * It also serves as a builder object for Invoices. 24 | */ 25 | @JsonIgnoreProperties(ignoreUnknown = true) 26 | public class Invoice implements Parcelable { 27 | 28 | static private ObjectMapper mapper = new ObjectMapper(); 29 | 30 | @Override 31 | public int describeContents() { 32 | return 0; 33 | } 34 | 35 | /** 36 | * Serializes the invoice into a Parcel 37 | * 38 | * @param dest 39 | * @param flags 40 | */ 41 | @Override 42 | public void writeToParcel(Parcel dest, int flags) { 43 | dest.writeLong(nonce); 44 | dest.writeString(guid); 45 | dest.writeString(token); 46 | dest.writeDouble(price); 47 | dest.writeString(currency); 48 | dest.writeString(posData); 49 | dest.writeString(notificationURL); 50 | dest.writeString(transactionSpeed); 51 | 52 | dest.writeInt(fullNotifications ? 1 : 0); 53 | dest.writeString(notificationEmail); 54 | dest.writeString(redirectURL); 55 | dest.writeString(orderId); 56 | dest.writeString(itemDesc); 57 | dest.writeString(itemCode); 58 | dest.writeInt(physical ? 1 : 0); 59 | 60 | dest.writeParcelable(buyerInfo, 0); 61 | 62 | dest.writeString(id); 63 | dest.writeString(url); 64 | dest.writeString(status); 65 | dest.writeString(btcPrice); 66 | dest.writeString(invoiceTime); 67 | dest.writeString(expirationTime); 68 | dest.writeString(currentTime); 69 | dest.writeString(btcPaid); 70 | dest.writeString(btcPrice); 71 | 72 | dest.writeString(rate); 73 | dest.writeString(exceptionStatus); 74 | 75 | dest.writeInt(transactions == null ? 0 : transactions.size()); 76 | if (transactions != null) { 77 | for (InvoiceTransaction transaction : transactions) { 78 | dest.writeParcelable(transaction, 0); 79 | } 80 | } 81 | 82 | dest.writeInt(exRates == null ? 0 : exRates.size()); 83 | if (exRates != null) { 84 | for (Map.Entry entry : exRates.entrySet()) { 85 | dest.writeString(entry.getKey()); 86 | dest.writeString(entry.getValue()); 87 | } 88 | } 89 | dest.writeParcelable(paymentUrls, 0); 90 | if (refundAddresses == null) { 91 | dest.writeStringList(new ArrayList()); 92 | } else { 93 | dest.writeStringList(refundAddresses); 94 | } 95 | } 96 | 97 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 98 | public Invoice createFromParcel(Parcel in) { 99 | 100 | Invoice invoice = new Invoice(); 101 | invoice.setNonce(in.readLong()); 102 | invoice.setGuid(in.readString()); 103 | invoice.setToken(in.readString()); 104 | invoice.setPrice(in.readDouble()); 105 | try { 106 | invoice.setCurrency(in.readString()); 107 | } catch (BitPayException e) { 108 | throw new RuntimeException(e); 109 | } 110 | invoice.setPosData(in.readString()); 111 | invoice.setNotificationURL(in.readString()); 112 | invoice.setTransactionSpeed(in.readString()); 113 | 114 | Integer read = in.readInt(); 115 | if (read != null && read == 1) { 116 | invoice.setFullNotifications(true); 117 | } 118 | invoice.setNotificationEmail(in.readString()); 119 | invoice.setRedirectURL(in.readString()); 120 | invoice.setOrderId(in.readString()); 121 | invoice.setItemDesc(in.readString()); 122 | invoice.setItemCode(in.readString()); 123 | read = in.readInt(); 124 | if (read != null && read == 1) { 125 | invoice.setPhysical(true); 126 | } 127 | 128 | invoice.setBuyerInfo((BuyerInfo) in.readParcelable(getClass().getClassLoader())); 129 | 130 | invoice.setId(in.readString()); 131 | invoice.setUrl(in.readString()); 132 | invoice.setStatus(in.readString()); 133 | invoice.setBtcPrice(in.readString()); 134 | invoice.setInvoiceTime(in.readString()); 135 | invoice.setExpirationTime(in.readString()); 136 | invoice.setCurrentTime(in.readString()); 137 | invoice.setBtcPaid(in.readString()); 138 | invoice.setBtcDue(in.readString()); 139 | 140 | invoice.setRate(in.readString()); 141 | invoice.setExceptionStatus(in.readString()); 142 | 143 | invoice.setTransactions(new ArrayList()); 144 | read = in.readInt(); 145 | for (int i = 0; i < read; i++) { 146 | invoice.transactions.add(in.readParcelable(getClass().getClassLoader())); 147 | } 148 | 149 | invoice.exRates = new Hashtable(); 150 | read = in.readInt(); 151 | for (int i = 0; i < read; i++) { 152 | String key = in.readString(); 153 | String value = in.readString(); 154 | invoice.exRates.put(key, value); 155 | } 156 | 157 | invoice.paymentUrls = in.readParcelable(getClass().getClassLoader()); 158 | invoice.refundAddresses = new ArrayList(); 159 | in.readStringList(invoice.refundAddresses); 160 | 161 | return invoice; 162 | } 163 | 164 | public Invoice[] newArray(int size) { 165 | return new Invoice[size]; 166 | } 167 | }; 168 | 169 | public static final String STATUS_NEW = "new"; 170 | public static final String STATUS_PAID = "paid"; 171 | public static final String STATUS_CONFIRMED = "confirmed"; 172 | public static final String STATUS_COMPLETE = "complete"; 173 | public static final String STATUS_INVALID = "invalid"; 174 | public static final String EXSTATUS_FALSE = "false"; 175 | public static final String EXSTATUS_PAID_OVER = "paidOver"; 176 | public static final String EXSTATUS_PAID_PARTIAL = "paidPartial"; 177 | 178 | private Long nonce = 0L; 179 | private String guid = ""; 180 | private String token = ""; 181 | 182 | private Double price; 183 | private String currency; 184 | private String posData = ""; 185 | private String notificationURL = ""; 186 | private String transactionSpeed = ""; 187 | private boolean fullNotifications = false; 188 | private String notificationEmail = ""; 189 | private String redirectURL = ""; 190 | private String orderId = ""; 191 | private String itemDesc = ""; 192 | private String itemCode = ""; 193 | private boolean physical = false; 194 | 195 | private BuyerInfo buyerInfo; 196 | 197 | private String id; 198 | private String url; 199 | private String status; 200 | private String btcPrice; 201 | private String invoiceTime; 202 | private String expirationTime; 203 | private String currentTime; 204 | private String btcPaid; 205 | private String btcDue; 206 | private List transactions; 207 | private String rate; 208 | private Hashtable exRates; 209 | private String exceptionStatus; 210 | private InvoicePaymentUrls paymentUrls; 211 | private String confirmations; 212 | private List refundAddresses; 213 | 214 | public Invoice() {} 215 | 216 | /** 217 | * Create an invoice with a price and a currency 218 | * 219 | * @param price the price to be paid 220 | * @param currency the ISO code for the currency this invoice is established in 221 | */ 222 | public Invoice(Double price, String currency) 223 | { 224 | this.price = price; 225 | this.currency = currency; 226 | } 227 | 228 | // API fields 229 | // 230 | 231 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 232 | public String getGuid() { 233 | return guid; 234 | } 235 | 236 | public void setGuid(String guid) { 237 | this.guid = guid; 238 | } 239 | 240 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 241 | public Long getNonce() { 242 | return nonce; 243 | } 244 | 245 | public void setNonce(Long nonce) { 246 | this.nonce = nonce; 247 | } 248 | 249 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 250 | public String getToken() { 251 | return token; 252 | } 253 | 254 | public void setToken(String token) { 255 | this.token = token; 256 | } 257 | 258 | // Required fields 259 | // 260 | 261 | @JsonProperty 262 | public Double getPrice() { 263 | return price; 264 | } 265 | 266 | public void setPrice(Double price) { 267 | this.price = price; 268 | } 269 | 270 | @JsonProperty 271 | public String getCurrency() { 272 | return currency; 273 | } 274 | 275 | public void setCurrency(String currency) throws BitPayException { 276 | if (currency.length() != 3) 277 | { 278 | throw new BitPayException("Error: currency code must be exactly three characters"); 279 | } 280 | this.currency = currency; 281 | } 282 | 283 | // Optional fields 284 | // 285 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 286 | public String getOrderId() { 287 | return orderId; 288 | } 289 | 290 | public void setOrderId(String orderId) { 291 | this.orderId = orderId; 292 | } 293 | 294 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 295 | public String getItemDesc() { 296 | return itemDesc; 297 | } 298 | 299 | public void setItemDesc(String itemDesc) { 300 | this.itemDesc = itemDesc; 301 | } 302 | 303 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 304 | public String getItemCode() { 305 | return itemCode; 306 | } 307 | 308 | public void setItemCode(String itemCode) { 309 | this.itemCode = itemCode; 310 | } 311 | 312 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 313 | public String getPosData() { 314 | return posData; 315 | } 316 | 317 | public void setPosData(String posData) { 318 | this.posData = posData; 319 | } 320 | 321 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 322 | public String getNotificationURL() { 323 | return notificationURL; 324 | } 325 | 326 | public void setNotificationURL(String notificationURL) { 327 | this.notificationURL = notificationURL; 328 | } 329 | 330 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 331 | public String getTransactionSpeed() { 332 | return transactionSpeed; 333 | } 334 | 335 | public void setTransactionSpeed(String transactionSpeed) { 336 | this.transactionSpeed = transactionSpeed; 337 | } 338 | 339 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 340 | public Boolean getFullNotifications() { 341 | return fullNotifications; 342 | } 343 | 344 | public void setFullNotifications(Boolean fullNotifications) { 345 | this.fullNotifications = fullNotifications; 346 | } 347 | 348 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 349 | public String getNotificationEmail() { 350 | return notificationEmail; 351 | } 352 | 353 | public void setNotificationEmail(String notificationEmail) { 354 | this.notificationEmail = notificationEmail; 355 | } 356 | 357 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 358 | public String getRedirectURL() { 359 | return redirectURL; 360 | } 361 | 362 | public void setRedirectURL(String redirectURL) { 363 | this.redirectURL = redirectURL; 364 | } 365 | 366 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 367 | public Boolean getPhysical() { 368 | return physical; 369 | } 370 | 371 | public void setPhysical(boolean physical) { 372 | this.physical = physical; 373 | } 374 | 375 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 376 | @JsonProperty("buyerFields") 377 | public BuyerInfo getBuyerInfo() { 378 | return buyerInfo; 379 | } 380 | 381 | @JsonProperty("buyerFields") 382 | public void setBuyerInfo(BuyerInfo buyerInfo) { 383 | this.buyerInfo = buyerInfo; 384 | } 385 | 386 | // Response fields 387 | // 388 | 389 | @JsonIgnore 390 | public String getBIP21due() { 391 | return getPaymentUrls().getBIP21().substring(0, paymentUrls.getBIP21().indexOf("?amount=") + "?amount=".length()) + getBtcDue(); 392 | } 393 | 394 | public String getId() { 395 | return id; 396 | } 397 | 398 | public void setId(String id) { 399 | this.id = id; 400 | } 401 | 402 | public String getUrl() { 403 | return url; 404 | } 405 | 406 | public void setUrl(String url) { 407 | this.url = url; 408 | } 409 | 410 | public String getStatus() { 411 | return status; 412 | } 413 | 414 | public void setStatus(String status) { 415 | this.status = status; 416 | } 417 | 418 | public String getBtcPrice() { 419 | return btcPrice; 420 | } 421 | 422 | public void setBtcPrice(String btcPrice) { 423 | this.btcPrice = btcPrice; 424 | } 425 | 426 | public String getInvoiceTime() { 427 | return invoiceTime; 428 | } 429 | 430 | public void setInvoiceTime(String invoiceTime) { 431 | this.invoiceTime = invoiceTime; 432 | } 433 | 434 | public String getExpirationTime() { 435 | return expirationTime; 436 | } 437 | 438 | public void setExpirationTime(String expirationTime) { 439 | this.expirationTime = expirationTime; 440 | } 441 | 442 | public String getCurrentTime() { 443 | return currentTime; 444 | } 445 | 446 | public void setCurrentTime(String currentTime) { 447 | this.currentTime = currentTime; 448 | } 449 | 450 | public String getBtcPaid() { 451 | return btcPaid; 452 | } 453 | 454 | public void setBtcPaid(String btcPaid) { 455 | this.btcPaid = btcPaid; 456 | } 457 | 458 | public String getBtcDue() { 459 | return btcDue; 460 | } 461 | 462 | public void setBtcDue(String btcDue) { 463 | this.btcDue = btcDue; 464 | } 465 | 466 | public List getTransactions() { 467 | return transactions; 468 | } 469 | 470 | public void setTransactions(List transactions) { 471 | this.transactions = transactions; 472 | } 473 | 474 | public String getRate() { 475 | return rate; 476 | } 477 | 478 | public void setRate(String rate) { 479 | this.rate = rate; 480 | } 481 | 482 | public Hashtable getExRates() { 483 | return exRates; 484 | } 485 | 486 | public void setExRates(Hashtable exRates) { 487 | this.exRates = exRates; 488 | } 489 | 490 | public String getExceptionStatus() { 491 | return exceptionStatus; 492 | } 493 | 494 | public void setExceptionStatus(String exceptionStatus) { 495 | this.exceptionStatus = exceptionStatus; 496 | } 497 | 498 | public InvoicePaymentUrls getPaymentUrls() { 499 | return paymentUrls; 500 | } 501 | 502 | public void setPaymentUrls(InvoicePaymentUrls paymentUrls) { 503 | this.paymentUrls = paymentUrls; 504 | } 505 | 506 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 507 | public List getRefundAddresses() { 508 | return refundAddresses; 509 | } 510 | 511 | public void setRefundAddresses(List refundAddresses) { 512 | this.refundAddresses = refundAddresses; 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/InvoicePaymentUrls.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | 10 | /** 11 | * Describes BIPs 12 | */ 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class InvoicePaymentUrls implements Parcelable { 15 | 16 | private String bip21 = ""; 17 | private String bip72 = ""; 18 | private String bip72b = ""; 19 | private String bip73 = ""; 20 | 21 | public InvoicePaymentUrls() {} 22 | 23 | @JsonIgnore 24 | public String getBIP21() { 25 | return bip21; 26 | } 27 | 28 | @JsonProperty("BIP21") 29 | public void setBIP21(String _BIP21) { 30 | this.bip21 = _BIP21; 31 | } 32 | 33 | @JsonIgnore 34 | public String getBIP72() { 35 | return bip72; 36 | } 37 | 38 | @JsonProperty("BIP72") 39 | public void setBIP72(String _BIP72) { 40 | this.bip72 = _BIP72; 41 | } 42 | 43 | @JsonIgnore 44 | public String getBIP72b() { 45 | return bip72b; 46 | } 47 | 48 | @JsonProperty("BIP72b") 49 | public void setBIP72b(String _BIP72b) { 50 | this.bip72b = _BIP72b; 51 | } 52 | 53 | @JsonIgnore 54 | public String getBIP73() { 55 | return bip73; 56 | } 57 | 58 | @JsonProperty("BIP73") 59 | public void setBIP73(String _BIP73) { 60 | this.bip73 = _BIP73; 61 | } 62 | 63 | @Override 64 | public int describeContents() { 65 | return 0; 66 | } 67 | 68 | @Override 69 | public void writeToParcel(Parcel dest, int flags) { 70 | dest.writeString(bip21); 71 | dest.writeString(bip72); 72 | dest.writeString(bip72b); 73 | dest.writeString(bip73); 74 | } 75 | 76 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 77 | public InvoicePaymentUrls createFromParcel(Parcel in) { 78 | 79 | InvoicePaymentUrls urls = new InvoicePaymentUrls(); 80 | urls.setBIP21(in.readString()); 81 | urls.setBIP72(in.readString()); 82 | urls.setBIP72b(in.readString()); 83 | urls.setBIP73(in.readString()); 84 | return urls; 85 | } 86 | 87 | @Override 88 | public InvoicePaymentUrls[] newArray(int size) { 89 | return new InvoicePaymentUrls[size]; 90 | } 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/InvoiceTransaction.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | 9 | /** 10 | * Stores information about when (in which transaction in the blockchain was an invoice paid, 11 | * partially or totally. 12 | */ 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) 15 | public class InvoiceTransaction implements Parcelable { 16 | 17 | private String txid; 18 | private String type; 19 | private double amount; 20 | private Integer confirmations; 21 | private String time; 22 | private String receivedTime; 23 | 24 | public InvoiceTransaction() {} 25 | 26 | public String getTxid() { 27 | return txid; 28 | } 29 | public void setTxid(String _txid) { 30 | this.txid = _txid; 31 | } 32 | public String getType() { 33 | return type; 34 | } 35 | public void setType(String _type) { 36 | this.type = _type; 37 | } 38 | public double getAmount() { 39 | return amount; 40 | } 41 | public void setAmount(double _amount) { 42 | this.amount = _amount; 43 | } 44 | public Integer getConfirmations() { 45 | return confirmations; 46 | } 47 | public void setConfirmations(Integer confirmations) { 48 | this.confirmations = confirmations; 49 | } 50 | public String getTime() { 51 | return time; 52 | } 53 | public void setTime(String time) { 54 | this.time = time; 55 | } 56 | public String getReceivedTime() { 57 | return receivedTime; 58 | } 59 | public void setReceivedTime(String receivedTime) { 60 | this.receivedTime = receivedTime; 61 | } 62 | 63 | @Override 64 | public int describeContents() { 65 | return 0; 66 | } 67 | 68 | @Override 69 | public void writeToParcel(Parcel dest, int flags) { 70 | dest.writeString(txid); 71 | dest.writeString(type); 72 | dest.writeDouble(amount); 73 | dest.writeInt(confirmations == null ? 0 : confirmations); 74 | dest.writeString(time); 75 | dest.writeString(receivedTime); 76 | } 77 | 78 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 79 | public InvoiceTransaction createFromParcel(Parcel in) { 80 | 81 | InvoiceTransaction transaction = new InvoiceTransaction(); 82 | transaction.setTxid(in.readString()); 83 | transaction.setTxid(in.readString()); 84 | transaction.setAmount(in.readDouble()); 85 | transaction.setConfirmations(in.readInt()); 86 | transaction.setTxid(in.readString()); 87 | transaction.setTxid(in.readString()); 88 | return transaction; 89 | } 90 | 91 | @Override 92 | public InvoiceTransaction[] newArray(int size) { 93 | return new InvoiceTransaction[size]; 94 | } 95 | }; 96 | } -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/Policy.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.List; 8 | 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class Policy { 11 | 12 | private String _policy; 13 | private String _method; 14 | private List _params; 15 | 16 | public Policy() {} 17 | 18 | @JsonIgnore 19 | public String getPolicy() 20 | { 21 | return _policy; 22 | } 23 | 24 | @JsonProperty("policy") 25 | public void setPolicy(String _policy) 26 | { 27 | this._policy = _policy; 28 | } 29 | 30 | @JsonIgnore 31 | public String getMethod() 32 | { 33 | return _method; 34 | } 35 | 36 | @JsonProperty("method") 37 | public void setMethod(String _method) 38 | { 39 | this._method = _method; 40 | } 41 | 42 | @JsonIgnore 43 | public List getParams() 44 | { 45 | return _params; 46 | } 47 | 48 | @JsonProperty("params") 49 | public void setParams(List _params) 50 | { 51 | this._params = _params; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/Rate.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | /** 8 | * Stores the exchange rate for a given currency. 9 | */ 10 | @JsonIgnoreProperties(ignoreUnknown = true) 11 | public class Rate { 12 | 13 | private String _name; 14 | private String _code; 15 | private double _value; 16 | 17 | public Rate() {} 18 | 19 | @JsonIgnore 20 | public String getName() { 21 | return _name; 22 | } 23 | 24 | @JsonProperty("name") 25 | public void setName(String _name) { 26 | this._name = _name; 27 | } 28 | 29 | @JsonIgnore 30 | public String getCode() { 31 | return _code; 32 | } 33 | 34 | @JsonProperty("code") 35 | public void setCode(String _code) { 36 | this._code = _code; 37 | } 38 | 39 | @JsonIgnore 40 | public double getValue() { 41 | return _value; 42 | } 43 | 44 | @JsonProperty("rate") 45 | public void setValue(double _value) { 46 | this._value = _value; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return _name; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/Rates.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Hold information about rates for multiple currencies. 7 | */ 8 | public class Rates { 9 | 10 | private List _rates; 11 | 12 | public Rates(List rates) { 13 | _rates = rates; 14 | } 15 | 16 | public List getRates() 17 | { 18 | return _rates; 19 | } 20 | 21 | public void update(List rates) { 22 | _rates = rates; 23 | } 24 | 25 | public double getRate(String currencyCode) { 26 | double val = 0; 27 | for (Rate rateObj : _rates) 28 | { 29 | if (rateObj.getCode().equals(currencyCode)) 30 | { 31 | val = rateObj.getValue(); 32 | break; 33 | } 34 | } 35 | return val; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android-sdk/src/main/java/com/bitpay/sdk/model/Token.java: -------------------------------------------------------------------------------- 1 | package com.bitpay.sdk.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * A token is used to access the BitPay API and authorize access to a facade to a private key. 12 | */ 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class Token { 15 | 16 | private String _guid; 17 | private long _nonce = 0; 18 | private String _id = ""; 19 | private String _pairingCode = ""; 20 | private String _facade = ""; 21 | private String _label = ""; 22 | private String _count = ""; 23 | private List _policies; 24 | private String _resource; 25 | private String _value; 26 | private String _dateCreated; 27 | private String pairingExpiration; 28 | 29 | public Token() {} 30 | 31 | // API fields 32 | // 33 | 34 | @JsonProperty("guid") 35 | public String getGuid() { 36 | return _guid; 37 | } 38 | 39 | @JsonProperty("guid") 40 | public void setGuid(String _guid) { 41 | this._guid = _guid; 42 | } 43 | 44 | @JsonProperty("nonce") 45 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 46 | public long getNonce() { 47 | return _nonce; 48 | } 49 | 50 | @JsonProperty("nonce") 51 | public void setNonce(long _nonce) { 52 | this._nonce = _nonce; 53 | } 54 | 55 | // Required fields 56 | // 57 | 58 | @JsonProperty("id") 59 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 60 | public String getId() { 61 | return _id; 62 | } 63 | 64 | @JsonProperty("id") 65 | public void setId(String _id) { 66 | this._id = _id; 67 | } 68 | 69 | @JsonProperty("pairingCode") 70 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 71 | public String getPairingCode() { 72 | return _pairingCode; 73 | } 74 | 75 | @JsonProperty("pairingCode") 76 | public void setPairingCode(String _pairingCode) { 77 | this._pairingCode = _pairingCode; 78 | } 79 | 80 | @JsonProperty("facade") 81 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 82 | public String getFacade() { 83 | return _facade; 84 | } 85 | 86 | @JsonProperty("facade") 87 | public void setFacade(String _facade) { 88 | this._facade = _facade; 89 | } 90 | 91 | @JsonProperty("label") 92 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 93 | public String getLabel() { 94 | return _label; 95 | } 96 | 97 | @JsonProperty("label") 98 | public void setLabel(String _label) { 99 | this._label = _label; 100 | } 101 | 102 | @JsonProperty("count") 103 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 104 | public String getCount() { 105 | return _count; 106 | } 107 | 108 | @JsonProperty("count") 109 | public void setCount(String _count) { 110 | this._count = _count; 111 | } 112 | 113 | // Response fields 114 | // 115 | 116 | @JsonIgnore 117 | public List getPolicies() { 118 | return _policies; 119 | } 120 | 121 | @JsonProperty("policies") 122 | public void setPolicies(List _policies) { 123 | this._policies = _policies; 124 | } 125 | 126 | @JsonIgnore 127 | public String getResource() { 128 | return _resource; 129 | } 130 | 131 | @JsonProperty("resource") 132 | public void setResource(String _resource) { 133 | this._resource = _resource; 134 | } 135 | 136 | @JsonIgnore 137 | public String getValue() { 138 | return _value; 139 | } 140 | 141 | @JsonProperty("token") 142 | public void setValue(String _value) { 143 | this._value = _value; 144 | } 145 | 146 | @JsonIgnore 147 | public String getDateCreated() { 148 | return _dateCreated; 149 | } 150 | 151 | @JsonProperty("dateCreated") 152 | public void setDateCreated(String _dateCreated) { 153 | this._dateCreated = _dateCreated; 154 | } 155 | 156 | @JsonProperty("pairingExpiration") 157 | public void setPairingExpiration(String expiration) { 158 | this.pairingExpiration = expiration; 159 | } 160 | 161 | @JsonIgnore 162 | public String getPairingExpiration() { 163 | return pairingExpiration; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-hdpi/accept_btc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-hdpi/accept_btc.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-mdpi/accept_btc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-mdpi/accept_btc.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xhdpi/accept_btc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-xhdpi/accept_btc.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xxhdpi/accept_btc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-xxhdpi/accept_btc.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/android-sdk/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xxhdpi/roundedbutton.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /android-sdk/src/main/res/drawable-xxhdpi/roundedbutton_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /android-sdk/src/main/res/layout/activity_invoice.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | 24 | 25 | 32 | 36 | 37 | 49 | 50 | 59 | 60 | 61 |