├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 | 1.6
51 |
52 |
53 |
54 |
55 |
56 |
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 | [](https://raw.githubusercontent.com/bitpay/android-sdk/master/LICENSE)
2 | [](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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 |
72 |
73 |
89 |
90 |
98 |
99 |
108 |
109 |
121 |
122 |
123 |
124 |
132 |
138 |
145 |
154 |
155 |
156 |
157 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/android-sdk/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #17223C
4 | #426D9E
5 | #FFFFFF
6 | #DBDBDB
7 | #bababa
8 |
--------------------------------------------------------------------------------
/android-sdk/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/android-sdk/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BitPay SDK
3 | Invoice Details
4 | Launch Wallet
5 |
6 |
--------------------------------------------------------------------------------
/android-sdk/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/bin/getClientToken:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /**
3 | *
4 | * BitPay API RPC Client to retrieve a vendor token for Android apps
5 | *
6 | */
7 | var https = require('https');
8 | var bitauth = require('bitauth');
9 |
10 | var token = process.argv[2];
11 | var test = process.argv.length > 3 ? process.argv[3] : undefined;
12 | if (test) {
13 | if (token === '--test') {
14 | var c = token;
15 | token = test;
16 | test = c;
17 | }
18 | }
19 | if (!token) {
20 | console.log('Pleace specify a token to pair with');
21 | process.exit(0);
22 | }
23 |
24 | var private_key = bitauth.generateSin().priv;
25 | var public_key = bitauth.getPublicKeyFromPrivateKey(private_key);
26 | var sin = bitauth.getSinFromPublicKey(public_key);
27 |
28 | var guid = (new Date()).getTime();
29 | var url = 'https://' + 'bitpay.com/tokens';
30 | var data = JSON.stringify({
31 | guid: Math.floor(guid%1e5) + '',
32 | id: sin,
33 | pairingCode: token,
34 | nonce: guid
35 | });
36 | var options = {
37 | hostname: (test ? 'test.' : '') + 'bitpay.com',
38 | port: 443,
39 | path: '/tokens',
40 | method: 'POST',
41 | headers: {
42 | "x-accept-version": "2.0.0",
43 | "x-bitpay-plugin-info": "BitPay Android Client",
44 | "content-type": "application/json",
45 | "accept": "application/json"
46 | }
47 | };
48 |
49 | var req = https.request(options, function(res) {
50 | res.setEncoding('utf8');
51 | res.on('data', function (chunk) {
52 | var body = JSON.parse(chunk);
53 | if (body.error) {
54 | console.log(chunk);
55 | } else {
56 | console.log("Successfully paired. Your client token is " + body.data[0].token);
57 | }
58 | });
59 | });
60 |
61 | req.on('error', function(e) {
62 | console.log('problem with request: ' + e.message);
63 | });
64 |
65 | // write data to request body
66 | req.write(data);
67 | req.end();
68 |
--------------------------------------------------------------------------------
/bitpay-android-sdk.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:1.5.0'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | mavenCentral()
14 | jcenter()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dist/android-sdk-1.0.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/dist/android-sdk-1.0.1.aar
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | sonatypeUsername=test
20 | sonatypePassword=test
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 09 11:42:59 EST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/sdk-android-bitpay.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':android-sdk', ':test'
2 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/test/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 20
5 | buildToolsVersion "20.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 19
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | }
16 | }
17 | packagingOptions {
18 | exclude 'META-INF/NOTICE'
19 | exclude 'META-INF/NOTICE.txt'
20 | exclude 'META-INF/LICENSE'
21 | exclude 'META-INF/LICENSE.txt'
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | compile project(':android-sdk')
28 | }
29 |
--------------------------------------------------------------------------------
/test/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 |
--------------------------------------------------------------------------------
/test/src/androidTest/java/com/bitpay/sdk/test/BitPayAndroidTest.java:
--------------------------------------------------------------------------------
1 | package com.bitpay.sdk.test;
2 |
3 | import android.test.AndroidTestCase;
4 |
5 | import com.bitpay.sdk.android.BitPayAndroid;
6 | import com.bitpay.sdk.android.interfaces.BitpayPromiseCallback;
7 | import com.bitpay.sdk.android.interfaces.PromiseCallback;
8 | import com.bitpay.sdk.controller.BitPayException;
9 | import com.bitpay.sdk.model.Invoice;
10 |
11 | import java.util.concurrent.CountDownLatch;
12 |
13 | /**
14 | * Created by eordano on 10/8/14.
15 | */
16 | public class BitPayAndroidTest extends AndroidTestCase {
17 | public static final String invoiceToken = "MuwmfQQE54MqGquaR6WtzV";
18 | public static final String privateKey = "b64a9314661c62de31e79607d2e426cc2bd7a68b2f8c58af8ee17f1bc2635dc8";
19 | public static final Double TOLERANCE = 0.01;
20 |
21 | public Exception createInvoiceError;
22 | public Exception createClientError;
23 | public Exception createClient2Error;
24 |
25 | public void testCreateInvoice() {
26 |
27 | final CountDownLatch latch = new CountDownLatch(1);
28 | BitPayAndroid.withToken(invoiceToken, "https://test.bitpay.com/").then(new BitpayPromiseCallback() {
29 | @Override
30 | public void onSuccess(BitPayAndroid client) {
31 | client.createNewInvoice(new Invoice(10.00, "USD")).then(new PromiseCallback() {
32 | @Override
33 | public void onSuccess(Invoice promised) {
34 | assertEquals(promised.getCurrency(), "USD");
35 | assertTrue(Math.abs(promised.getPrice() - 10.00) < TOLERANCE);
36 | latch.countDown();
37 | }
38 |
39 | @Override
40 | public void onError(BitPayException e) {
41 | createInvoiceError = e;
42 | latch.countDown();
43 | }
44 | });
45 | }
46 |
47 | @Override
48 | public void onError(BitPayException e) {
49 | createInvoiceError = e;
50 | latch.countDown();
51 | }
52 | });
53 | try {
54 | latch.await();
55 | } catch (InterruptedException e) {
56 | throw new RuntimeException(e);
57 | }
58 | assertNull(createInvoiceError);
59 | }
60 | public void testCreateClient() {
61 |
62 | final CountDownLatch latch = new CountDownLatch(1);
63 | BitPayAndroid.withToken(invoiceToken, "https://test.bitpay.com/").then(new BitpayPromiseCallback() {
64 | @Override
65 | public void onSuccess(BitPayAndroid client) {
66 | assertNotNull(client);
67 | latch.countDown();
68 | }
69 |
70 | @Override
71 | public void onError(BitPayException e) {
72 | createClientError = e;
73 | latch.countDown();
74 | }
75 | });
76 | try {
77 | latch.await();
78 | } catch (InterruptedException e) {
79 | throw new RuntimeException(e);
80 | }
81 | assertNull(createClientError);
82 | }
83 | public void testCreateClientWithIdentity() {
84 | final CountDownLatch latch = new CountDownLatch(1);
85 | BitPayAndroid.getClient(privateKey, "https://test.bitpay.com/").then(new BitpayPromiseCallback() {
86 | @Override
87 | public void onSuccess(BitPayAndroid client) {
88 | assertNotNull(client);
89 | assertEquals(client.getPrivateKey(), privateKey);
90 | latch.countDown();
91 | }
92 |
93 | @Override
94 | public void onError(BitPayException e) {
95 | createClient2Error = e;
96 | latch.countDown();
97 | }
98 | });
99 | try {
100 | latch.await();
101 | } catch (InterruptedException e) {
102 | throw new RuntimeException(e);
103 | }
104 | assertNull(createClient2Error);
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/test/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/test/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/test/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/test/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/test/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/test/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/test/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitpay/android-sdk/60231dce6f4c7f053bbffd0f1f0c0f9a738b4401/test/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/test/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | test
3 |
4 |
--------------------------------------------------------------------------------
/test/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------