Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cc41ff0

Browse files
author
Kevin Hellemun
committedMar 20, 2018
Initial commit.
1 parent b8eb1be commit cc41ff0

22 files changed

+1845
-0
lines changed
 

‎.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Created by https://www.gitignore.io/api/java
2+
3+
### Java ###
4+
# Compiled class file
5+
*.class
6+
7+
# Log file
8+
*.log
9+
10+
# BlueJ files
11+
*.ctxt
12+
13+
# Mobile Tools for Java (J2ME)
14+
.mtj.tmp/
15+
16+
# Package Files #
17+
*.jar
18+
*.war
19+
*.ear
20+
*.zip
21+
*.tar.gz
22+
*.rar
23+
24+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
25+
hs_err_pid*
26+
27+
28+
# End of https://www.gitignore.io/api/java

‎base.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
3+
# check if stdout is a terminal
4+
if [ -t 1 ]; then
5+
supported_color_count="$(tput colors)"
6+
7+
if [ -n "$supported_color_count" ] && [ "$supported_color_count" -ge 8 ]; then
8+
# All ANSI color / format constants.
9+
ANSI_ESCAPE="\033"
10+
ANSI_FORMAT_BEGIN="${ANSI_ESCAPE}["
11+
ANSI_FORMAT_END="m"
12+
ANSI_FORMAT_CLEAR="${ANSI_FORMAT_BEGIN}0${ANSI_FORMAT_END}"
13+
ANSI_FORMAT_BRIGHT="${ANSI_FORMAT_BEGIN}1${ANSI_FORMAT_END}"
14+
ANSI_FORMAT_DIM="${ANSI_FORMAT_BEGIN}2${ANSI_FORMAT_END}"
15+
ANSI_FORMAT_INVERSE="${ANSI_FORMAT_BEGIN}7${ANSI_FORMAT_END}"
16+
ANSI_FORMAT_VERBOSE="${ANSI_FORMAT_BRIGHT}${ANSI_FORMAT_INVERSE}"
17+
ANSI_FORMAT_COLOR_GREEN_LIGHT="${ANSI_FORMAT_BEGIN}92${ANSI_FORMAT_END}"
18+
ANSI_FORMAT_COLOR_YELLOW="${ANSI_FORMAT_BEGIN}93${ANSI_FORMAT_END}"
19+
ANSI_FORMAT_COLOR_BLUE_LIGHT="${ANSI_FORMAT_BEGIN}94${ANSI_FORMAT_END}"
20+
ANSI_FORMAT_COLOR_CYAN_LIGHT="${ANSI_FORMAT_BEGIN}96${ANSI_FORMAT_END}"
21+
fi
22+
fi

‎build.gradle

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
group 'com.bunq.tinker'
2+
version '0.0.1'
3+
4+
apply plugin: 'java'
5+
apply plugin: 'maven'
6+
sourceCompatibility = 1.8
7+
8+
repositories {
9+
mavenCentral()
10+
maven { url 'https://jitpack.io' }
11+
}
12+
13+
dependencies {
14+
testCompile group: 'junit', name: 'junit', version: '4.12'
15+
// https://mvnrepository.com/artifact/commons-cli/commons-cli
16+
compile group: 'commons-cli', name: 'commons-cli', version: '1.2'
17+
compile 'com.github.bunq:sdk_java:0.12.4'
18+
}
19+
20+
jar {
21+
manifest {
22+
attributes ('Main-Class': 'com.bunq.tinker.utils.TinkerRunner',
23+
"Class-Path": configurations.compile.collect { it.absolutePath }.join(' ')
24+
)
25+
}
26+
}
27+
28+
configurations {
29+
jar.archiveName = 'tinker.jar'
30+
}
31+
32+
apply plugin: 'idea'

‎build/tmp/jar/MANIFEST.MF

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Manifest-Version: 1.0
2+

‎go-pro

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#!/usr/bin/env bash
2+
3+
WORKING_DIR="$(cd `dirname $0`; pwd)"
4+
SCRIPT_NAME="$(basename $0)"
5+
source "${WORKING_DIR}/base.sh"
6+
7+
# Header constants.
8+
HEADER_GO_PRO="${ANSI_FORMAT_COLOR_YELLOW}
9+
██████╗ ██████╗ ██████╗ ██████╗ ██████╗
10+
██╔════╝ ██╔═══██╗ ██╔══██╗██╔══██╗██╔═══██╗
11+
██║ ███╗██║ ██║ ██████╔╝██████╔╝██║ ██║
12+
██║ ██║██║ ██║ ██╔═══╝ ██╔══██╗██║ ██║
13+
╚██████╔╝╚██████╔╝ ██║ ██║ ██║╚██████╔╝
14+
╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
15+
${ANSI_FORMAT_CLEAR}"
16+
FOOTER_PRODUCTION_READY="${ANSI_FORMAT_COLOR_GREEN_LIGHT}
17+
🌈 You're ready to use the PRODUCTION environment.
18+
${ANSI_FORMAT_CLEAR}"
19+
20+
# Usage constant.
21+
USAGE="\
22+
┌───────────────────────┬──────────────────────────────────────────────────────────────┐
23+
│ │ Run │
24+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
25+
│ ✅ Show Overview │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar UserOverview --production${ANSI_FORMAT_CLEAR}
26+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
27+
│ 🔼 Make a payment │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar MakePayment --production${ANSI_FORMAT_CLEAR}
28+
│ │ │
29+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
30+
│ │ ${ANSI_FORMAT_DIM}--amount [euro]${ANSI_FORMAT_CLEAR}
31+
│ │ ${ANSI_FORMAT_DIM}--recipient [email]${ANSI_FORMAT_CLEAR}
32+
│ │ ${ANSI_FORMAT_DIM}--description [string]${ANSI_FORMAT_CLEAR}
33+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
34+
│ 🔽 Make a request │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar MakeRequest --production${ANSI_FORMAT_CLEAR}
35+
│ │ │
36+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
37+
│ │ ${ANSI_FORMAT_DIM}--amount [euro]${ANSI_FORMAT_CLEAR}
38+
│ │ ${ANSI_FORMAT_DIM}--recipient [email]${ANSI_FORMAT_CLEAR}
39+
│ │ ${ANSI_FORMAT_DIM}--description [string]${ANSI_FORMAT_CLEAR}
40+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
41+
│ 🔗 Link Card │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar LinkCard --production${ANSI_FORMAT_CLEAR}
42+
│ │ │
43+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
44+
│ │ ${ANSI_FORMAT_DIM}--card-id [id]${ANSI_FORMAT_CLEAR}
45+
│ │ ${ANSI_FORMAT_DIM}--account-id [id]${ANSI_FORMAT_CLEAR}
46+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
47+
│ ✏️ Update Account │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar UpdateAccount --production${ANSI_FORMAT_CLEAR}
48+
│ │ │
49+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
50+
│ │ ${ANSI_FORMAT_DIM}--account-id [id]${ANSI_FORMAT_CLEAR}
51+
│ │ ${ANSI_FORMAT_DIM}--name [string]${ANSI_FORMAT_CLEAR}
52+
├───────────────────────┼──────────────────────────────────────────────────────────────┤
53+
│ 📫️ Add Callback Url │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar AddCallbackUrl --production${ANSI_FORMAT_CLEAR}
54+
│ │ │
55+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
56+
│ │ ${ANSI_FORMAT_DIM}--url [url]${ANSI_FORMAT_CLEAR}
57+
└───────────────────────┴──────────────────────────────────────────────────────────────┘
58+
"
59+
60+
# Endpoint constants.
61+
API_TINKER='https://api.tinker.bunq.com/v1/'
62+
63+
# Status constants.
64+
STATUS_CREATED="CREATED"
65+
STATUS_ACCEPTED="ACCEPTED"
66+
67+
function shouldEnrollProductionUser
68+
{
69+
[ ! -f "${WORKING_DIR}/bunq-production.conf" ] && return 0 || return 1
70+
}
71+
72+
function enrollProductionUser
73+
{
74+
response="$(createUserCredentialPasswordIpRequest)"
75+
publicUuid="$(determineUserCredentialPasswordIpRequestPublicUuidFromResponse ${response})"
76+
token="$(determineUserCredentialPasswordIpRequestTokenQrFromResponse ${response})"
77+
78+
displayTokenQr "${token}"
79+
waitForUserCredentialPasswordIpRequestAccepted "${publicUuid}"
80+
clearTokenQr "${token}"
81+
apiKey="$(getApiKeyFromUserCredentialPasswordIpRequestAccepted "${publicUuid}")"
82+
java -jar tinker/tinker.jar CreateProductionConfiguration --api-key "${apiKey}"
83+
}
84+
85+
function createUserCredentialPasswordIpRequest
86+
{
87+
echo "$(curl -k -s -X POST -H "X-Bunq-Client-Request-Id: unique" ${API_TINKER}/credential-password-ip-request)"
88+
}
89+
90+
function determineUserCredentialPasswordIpRequestPublicUuidFromResponse
91+
{
92+
echo "$(echo ${1} | jq -r '.Response | .[0].UserCredentialPasswordIpRequest.uuid')"
93+
}
94+
95+
function determineUserCredentialPasswordIpRequestTokenQrFromResponse
96+
{
97+
echo "$(echo ${1} | jq -r '.Response | .[0].UserCredentialPasswordIpRequest.qr_token')"
98+
}
99+
100+
function determineUserCredentialPasswordIpRequestStatusFromResponse
101+
{
102+
echo "$(echo ${1} | jq -r '.Response | .[0].UserCredentialPasswordIpRequest.status')"
103+
}
104+
105+
function determineUserCredentialPasswordIpRequestApiKeyFromResponse
106+
{
107+
echo "$(echo ${1} | jq -r '.Response | .[0].UserCredentialPasswordIpRequest.api_key')"
108+
}
109+
110+
function displayTokenQr
111+
{
112+
token="${1}"
113+
echo -e "$(qrencode --type ANSI256 --margin 1 "18:${token}")"
114+
echo ""
115+
echo -e " ${ANSI_FORMAT_COLOR_GREEN_LIGHT}Scan this QR with your bunq app${ANSI_FORMAT_CLEAR}"
116+
echo ""
117+
}
118+
119+
function clearTokenQr
120+
{
121+
token="${1}"
122+
countAllLineToClear="$(displayTokenQr "${token}" | wc -l)"
123+
124+
clearLines "${countAllLineToClear}"
125+
}
126+
127+
function clearLines
128+
{
129+
countAllLineToClear="${1}"
130+
131+
seq 1 "${countAllLineToClear}" | while read i; do echo -en "\033[1A\r\033[K"; done
132+
}
133+
134+
function waitForUserCredentialPasswordIpRequestAccepted
135+
{
136+
uuid="${1}"
137+
while ! isUserCredentialPasswordIpRequestAccepted "${uuid}" ; do
138+
sleep 1
139+
done
140+
}
141+
142+
function isUserCredentialPasswordIpRequestAccepted
143+
{
144+
uuid="${1}"
145+
response="$(curl -k -s -X GET -H "X-Bunq-Client-Request-Id: unique" ${API_TINKER}/credential-password-ip-request/${uuid})"
146+
status="$(determineUserCredentialPasswordIpRequestStatusFromResponse "${response}")"
147+
if [ "${status}" = "${STATUS_ACCEPTED}" ]; then
148+
return 0
149+
else
150+
return 1
151+
fi
152+
}
153+
154+
function getApiKeyFromUserCredentialPasswordIpRequestAccepted
155+
{
156+
uuid="${1}"
157+
response="$(curl -k -s -X GET -H "X-Bunq-Client-Request-Id: unique" ${API_TINKER}/credential-password-ip-request/${uuid})"
158+
status="$(determineUserCredentialPasswordIpRequestStatusFromResponse "${response}")"
159+
echo "$(determineUserCredentialPasswordIpRequestApiKeyFromResponse "${response}")"
160+
}
161+
162+
echo -e "${HEADER_GO_PRO}"
163+
164+
if shouldEnrollProductionUser; then
165+
enrollProductionUser
166+
else
167+
# Already have a production config.
168+
:
169+
fi
170+
clearLines 1
171+
echo -e "${FOOTER_PRODUCTION_READY}"
172+
sleep 1
173+
echo -e "${USAGE}"

‎go-tinker

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env bash
2+
3+
WORKING_DIR="$(cd `dirname $0`; pwd)"
4+
SCRIPT_NAME="$(basename $0)"
5+
source "${WORKING_DIR}/base.sh"
6+
7+
HEADER="${ANSI_FORMAT_COLOR_BLUE_LIGHT}
8+
██████╗ ██████╗ ████████╗██╗███╗ ██╗██╗ ██╗███████╗██████╗
9+
██╔════╝ ██╔═══██╗ ╚══██╔══╝██║████╗ ██║██║ ██╔╝██╔════╝██╔══██╗
10+
██║ ███╗██║ ██║ ██║ ██║██╔██╗ ██║█████╔╝ █████╗ ██████╔╝
11+
██║ ██║██║ ██║ ██║ ██║██║╚██╗██║██╔═██╗ ██╔══╝ ██╔══██╗
12+
╚██████╔╝╚██████╔╝ ██║ ██║██║ ╚████║██║ ██╗███████╗██║ ██║
13+
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
14+
${ANSI_FORMAT_CLEAR}${ANSI_FORMAT_COLOR_CYAN_LIGHT}
15+
🎨 Tinker the bunq API - An API so powerful that we built bunq on it
16+
${ANSI_FORMAT_CLEAR}"
17+
18+
USAGE="\
19+
┌───────────────────────┬─────────────────────────────────────────────────┐
20+
│ │ Run │
21+
├───────────────────────┼─────────────────────────────────────────────────┤
22+
│ ✅ Show Overview │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar UserOverview${ANSI_FORMAT_CLEAR}
23+
├───────────────────────┼─────────────────────────────────────────────────┤
24+
│ 🔼 Make a payment │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar MakePayment${ANSI_FORMAT_CLEAR}
25+
│ │ │
26+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
27+
│ │ ${ANSI_FORMAT_DIM}--amount [euro]${ANSI_FORMAT_CLEAR}
28+
│ │ ${ANSI_FORMAT_DIM}--recipient [email]${ANSI_FORMAT_CLEAR}
29+
│ │ ${ANSI_FORMAT_DIM}--description [string]${ANSI_FORMAT_CLEAR}
30+
├───────────────────────┼─────────────────────────────────────────────────┤
31+
│ 🔽 Make a request │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar MakeRequest${ANSI_FORMAT_CLEAR}
32+
│ │ │
33+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
34+
│ │ ${ANSI_FORMAT_DIM}--amount [euro]${ANSI_FORMAT_CLEAR}
35+
│ │ ${ANSI_FORMAT_DIM}--recipient [email]${ANSI_FORMAT_CLEAR}
36+
│ │ ${ANSI_FORMAT_DIM}--description [string]${ANSI_FORMAT_CLEAR}
37+
├───────────────────────┼─────────────────────────────────────────────────┤
38+
│ 🔗 Link Card │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar LinkCard${ANSI_FORMAT_CLEAR}
39+
│ │ │
40+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
41+
│ │ ${ANSI_FORMAT_DIM}--card-id [id]${ANSI_FORMAT_CLEAR}
42+
│ │ ${ANSI_FORMAT_DIM}--account-id [id]${ANSI_FORMAT_CLEAR}
43+
├───────────────────────┼─────────────────────────────────────────────────┤
44+
│ ✏️ Update Account │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar UpdateAccount${ANSI_FORMAT_CLEAR}
45+
│ │ │
46+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
47+
│ │ ${ANSI_FORMAT_DIM}--account-id [id]${ANSI_FORMAT_CLEAR}
48+
│ │ ${ANSI_FORMAT_DIM}--name [string]${ANSI_FORMAT_CLEAR}
49+
├───────────────────────┼─────────────────────────────────────────────────┤
50+
│ 📫️ Add Callback Url │ ${ANSI_FORMAT_VERBOSE}java -jar tinker/tinker.jar AddCallbackUrl${ANSI_FORMAT_CLEAR}
51+
│ │ │
52+
│ │ ${ANSI_FORMAT_DIM}Additional parameters:${ANSI_FORMAT_CLEAR}
53+
│ │ ${ANSI_FORMAT_DIM}--url [url]${ANSI_FORMAT_CLEAR}
54+
└───────────────────────┴─────────────────────────────────────────────────┘
55+
56+
Check out the Source files and tinker with them!
57+
58+
${ANSI_FORMAT_DIM}Want to use PRODUCTION? Run: ./go-pro${ANSI_FORMAT_CLEAR}
59+
"
60+
61+
echo -e "${HEADER}"
62+
sleep 1
63+
echo -e "${USAGE}"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#Fri Mar 16 12:30:08 CET 2018
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-4.0-all.zip

‎gradlew

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env sh
2+
3+
##############################################################################
4+
##
5+
## Gradle start up script for UN*X
6+
##
7+
##############################################################################
8+
9+
# Attempt to set APP_HOME
10+
# Resolve links: $0 may be a link
11+
PRG="$0"
12+
# Need this for relative symlinks.
13+
while [ -h "$PRG" ] ; do
14+
ls=`ls -ld "$PRG"`
15+
link=`expr "$ls" : '.*-> \(.*\)$'`
16+
if expr "$link" : '/.*' > /dev/null; then
17+
PRG="$link"
18+
else
19+
PRG=`dirname "$PRG"`"/$link"
20+
fi
21+
done
22+
SAVED="`pwd`"
23+
cd "`dirname \"$PRG\"`/" >/dev/null
24+
APP_HOME="`pwd -P`"
25+
cd "$SAVED" >/dev/null
26+
27+
APP_NAME="Gradle"
28+
APP_BASE_NAME=`basename "$0"`
29+
30+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31+
DEFAULT_JVM_OPTS=""
32+
33+
# Use the maximum available, or set MAX_FD != -1 to use that value.
34+
MAX_FD="maximum"
35+
36+
warn () {
37+
echo "$*"
38+
}
39+
40+
die () {
41+
echo
42+
echo "$*"
43+
echo
44+
exit 1
45+
}
46+
47+
# OS specific support (must be 'true' or 'false').
48+
cygwin=false
49+
msys=false
50+
darwin=false
51+
nonstop=false
52+
case "`uname`" in
53+
CYGWIN* )
54+
cygwin=true
55+
;;
56+
Darwin* )
57+
darwin=true
58+
;;
59+
MINGW* )
60+
msys=true
61+
;;
62+
NONSTOP* )
63+
nonstop=true
64+
;;
65+
esac
66+
67+
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68+
69+
# Determine the Java command to use to start the JVM.
70+
if [ -n "$JAVA_HOME" ] ; then
71+
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72+
# IBM's JDK on AIX uses strange locations for the executables
73+
JAVACMD="$JAVA_HOME/jre/sh/java"
74+
else
75+
JAVACMD="$JAVA_HOME/bin/java"
76+
fi
77+
if [ ! -x "$JAVACMD" ] ; then
78+
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79+
80+
Please set the JAVA_HOME variable in your environment to match the
81+
location of your Java installation."
82+
fi
83+
else
84+
JAVACMD="java"
85+
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86+
87+
Please set the JAVA_HOME variable in your environment to match the
88+
location of your Java installation."
89+
fi
90+
91+
# Increase the maximum file descriptors if we can.
92+
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93+
MAX_FD_LIMIT=`ulimit -H -n`
94+
if [ $? -eq 0 ] ; then
95+
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96+
MAX_FD="$MAX_FD_LIMIT"
97+
fi
98+
ulimit -n $MAX_FD
99+
if [ $? -ne 0 ] ; then
100+
warn "Could not set maximum file descriptor limit: $MAX_FD"
101+
fi
102+
else
103+
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104+
fi
105+
fi
106+
107+
# For Darwin, add options to specify how the application appears in the dock
108+
if $darwin; then
109+
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110+
fi
111+
112+
# For Cygwin, switch paths to Windows format before running java
113+
if $cygwin ; then
114+
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115+
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116+
JAVACMD=`cygpath --unix "$JAVACMD"`
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+
# Escape application args
158+
save () {
159+
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160+
echo " "
161+
}
162+
APP_ARGS=$(save "$@")
163+
164+
# Collect all arguments for the java command, following the shell quoting and substitution rules
165+
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166+
167+
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168+
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169+
cd "$(dirname "$0")"
170+
fi
171+
172+
exec "$JAVACMD" "$@"

‎gradlew.bat

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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+
set DIRNAME=%~dp0
12+
if "%DIRNAME%" == "" set DIRNAME=.
13+
set APP_BASE_NAME=%~n0
14+
set APP_HOME=%DIRNAME%
15+
16+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17+
set DEFAULT_JVM_OPTS=
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 Windows variants
50+
51+
if not "%OS%" == "Windows_NT" goto win9xME_args
52+
53+
:win9xME_args
54+
@rem Slurp the command line arguments.
55+
set CMD_LINE_ARGS=
56+
set _SKIP=2
57+
58+
:win9xME_args_slurp
59+
if "x%~1" == "x" goto execute
60+
61+
set CMD_LINE_ARGS=%*
62+
63+
:execute
64+
@rem Setup the command line
65+
66+
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67+
68+
@rem Execute Gradle
69+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70+
71+
:end
72+
@rem End local scope for the variables with windows NT shell
73+
if "%ERRORLEVEL%"=="0" goto mainEnd
74+
75+
:fail
76+
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77+
rem the _cmd.exe /c_ return code!
78+
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79+
exit /b 1
80+
81+
:mainEnd
82+
if "%OS%"=="Windows_NT" endlocal
83+
84+
:omega

‎settings.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rootProject.name = 'tinker'
2+

‎setup.sh

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env bash
2+
3+
# Error constants.
4+
ERROR_SYSTEM_UNKNOWN='Unknown system found "%s".\n'
5+
ERROR_RUN_IN_EMPTY_DIRECTORY='Please run the script from an empty directory\n'
6+
ERROR_COULD_NOT_FIND_COMMAND='Could not find "%s", try installing it by running "%s".'
7+
8+
# System Constants
9+
SYSTEM_NAME_LINUX='Linux'
10+
SYSTEM_NAME_MAC_OS='Darwin'
11+
SYSTEM_NAME_FREEBSD='FreeBSD'
12+
ALL_SYSTEM_SUPPORTED="${SYSTEM_NAME_LINUX} ${SYSTEM_NAME_MAC_OS} ${SYSTEM_NAME_FREEBSD}"
13+
14+
# Prerequisite constants.
15+
PREREQUISITE_CONSTANT_PREFIX='ALL_PREREQUISITE_'
16+
ALL_PREREQUISITE_GLOBAL='git java gradle qrencode jq'
17+
ALL_PREREQUISITE_LINUX=''
18+
ALL_PREREQUISITE_DARWIN='brew'
19+
ALL_PREREQUISITE_FREEBSD=''
20+
21+
# Installation method constants.
22+
COMMAND_INSTALLATION_PREFIX='COMMAND_INSTALLATION_'
23+
COMMAND_INSTALLATION_LINUX='apt-get install'
24+
COMMAND_INSTALLATION_DARWIN='brew install'
25+
COMMAND_INSTALLATION_FREEBSD='pkg install'
26+
27+
# Version index
28+
INDEX_VERSION_MAJOR='0'
29+
INDEX_VERSION_MINOR='1'
30+
INDEX_VERSION_PATCH='2'
31+
32+
# Platform specific sed command
33+
COMMAND_SED_PREFIX='COMMAND_SED_'
34+
COMMAND_SED_LINUX='sed -rn'
35+
COMMAND_SED_DARWIN='sed -En'
36+
COMMAND_SED_FREEBSD='sed -rn'
37+
38+
function assertIsSystemSupported
39+
{
40+
contains "${ALL_SYSTEM_SUPPORTED}" "$(determineSystemName)" \
41+
&& return 0 || printf "${ERROR_SYSTEM_UNKNOWN}" "$(determineSystemName)" >&2 && exit 1
42+
}
43+
44+
function assertIsRanInEmptyDirectory
45+
{
46+
[ "$(ls -1A | wc -l)" -eq 0 ] && return 0 || printf "${ERROR_RUN_IN_EMPTY_DIRECTORY}" >&2 && exit 1
47+
}
48+
49+
function assertAllPrerequisitePresent
50+
{
51+
allPrerequisiteMissing="$(determineAllPrerequisiteMissing)"
52+
53+
if [ -z "${allPrerequisiteMissing}" ]; then
54+
# All prerequisites are available
55+
return 0
56+
else
57+
echo -n "$(determineInstructionInstallationAllPrerequisiteMissing "${allPrerequisiteMissing}")"
58+
59+
exit 1
60+
fi
61+
}
62+
63+
function determineAllPrerequisiteMissing
64+
{
65+
for prerequisite in $(determineAllPrerequisite); do
66+
which "${prerequisite}" > /dev/null || echo "${prerequisite}"
67+
done
68+
}
69+
70+
function determineAllPrerequisite
71+
{
72+
echo "$(determineAllPrerequisiteSystemSpecific) ${ALL_PREREQUISITE_GLOBAL}"
73+
}
74+
75+
function determineAllPrerequisiteSystemSpecific
76+
{
77+
prerequisiteConstantName="${PREREQUISITE_CONSTANT_PREFIX}$(capitalize $(determineSystemName))"
78+
echo "${!prerequisiteConstantName}"
79+
}
80+
81+
function determineInstructionInstallationAllPrerequisiteMissing
82+
{
83+
allPrerequisiteMissing="${1}"
84+
systemName="$(determineSystemName)"
85+
86+
for prerequisiteMissing in ${1}; do
87+
prerequisiteMissingCapitalized="$(capitalizeFirstLetter ${prerequisiteMissing})"
88+
89+
if declare -F "determineInstructionInstallation${prerequisiteMissingCapitalized}${systemName}" > /dev/null ; then
90+
echo "$(determineInstructionInstallation${prerequisiteMissingCapitalized}${systemName})" >&2
91+
elif declare -F "determineInstructionInstallation${prerequisiteMissingCapitalized}" > /dev/null ; then
92+
echo "$(determineInstructionInstallation${prerequisiteMissingCapitalized})" >&2
93+
else
94+
echo "$(determineInstructionInstallation "${prerequisiteMissing}" "${systemName}")" >&2
95+
fi
96+
done
97+
}
98+
99+
function determineInstructionInstallationBrewDarwin
100+
{
101+
echo -ne "Could not find \"brew\", install this by running: \033[1m/usr/bin/ruby -e \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\033[0m"
102+
echo ", or checkout how to install this by going to https://brew.sh."
103+
}
104+
105+
function determineInstructionInstallationPhpDarwin
106+
{
107+
determineInstructionInstallation "php" "$(determineSystemName)" "php70 --with-homebrew-curl"
108+
}
109+
110+
function determineInstructionInstallationJavaDarwin
111+
{
112+
echo "Could not find \"java\", check installation instructions at: https://docs.oracle.com/javase/9/install/installation-jdk-and-jre-macos.htm."
113+
}
114+
115+
function determineInstructionInstallationJavaLinux
116+
{
117+
determineInstructionInstallation "java" "$(determineSystemName)" "openjdk-8-jdk"
118+
}
119+
120+
function determineInstructionInstallationJavaFreebsd
121+
{
122+
determineInstructionInstallation "java" "$(determineSystemName)" "openjdk8"
123+
}
124+
125+
function determineInstructionInstallationQrencodeFreeBSD
126+
{
127+
determineInstructionInstallation "qrencode" "$(determineSystemName)" "libqrencode"
128+
}
129+
130+
function determineInstructionInstallation
131+
{
132+
programName="${1}"
133+
systemName="${2}"
134+
programPackageName=${3:-${programName}}
135+
commandInstallationConstantName="${COMMAND_INSTALLATION_PREFIX}$(capitalize ${systemName})"
136+
printf "${ERROR_COULD_NOT_FIND_COMMAND}" "${programName}" "${!commandInstallationConstantName} ${programPackageName}"
137+
}
138+
139+
function determineSystemName
140+
{
141+
echo "$(uname -s)"
142+
}
143+
144+
function determineSedCommand
145+
{
146+
commandSedConstantName="${COMMAND_SED_PREFIX}$(capitalize $(determineSystemName))"
147+
echo "${!commandSedConstantName}"
148+
}
149+
150+
function cloneTinkerJava
151+
{
152+
git clone https://github.com/bunq/tinker_java.git .
153+
git checkout master
154+
}
155+
156+
function gradleInstall
157+
{
158+
gradle install
159+
}
160+
161+
function createSymlink
162+
{
163+
ln -s build/libs tinker
164+
}
165+
166+
function startTinker
167+
{
168+
./go-tinker
169+
}
170+
171+
function contains
172+
{
173+
[[ ${1} =~ (^|[[:space:]])${2}($|[[:space:]]) ]] && return 0 || return 1
174+
}
175+
176+
function capitalize
177+
{
178+
echo "${@}" | tr [:lower:] [:upper:]
179+
}
180+
181+
function capitalizeFirstLetter
182+
{
183+
echo "$(capitalize ${1:0:1})${1:1}"
184+
}
185+
186+
assertIsSystemSupported
187+
assertIsRanInEmptyDirectory
188+
assertAllPrerequisitePresent
189+
cloneTinkerJava
190+
gradleInstall
191+
gradleBuild
192+
createSymlink
193+
startTinker
194+
195+
exit 0
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.exception.BunqException;
5+
import com.bunq.sdk.model.generated.endpoint.UserCompany;
6+
import com.bunq.sdk.model.generated.endpoint.UserLight;
7+
import com.bunq.sdk.model.generated.endpoint.UserPerson;
8+
import com.bunq.sdk.model.generated.object.NotificationFilter;
9+
import com.bunq.tinker.libs.BunqLib;
10+
import com.bunq.tinker.libs.SharedLib;
11+
import com.bunq.tinker.utils.ITinker;
12+
import org.apache.commons.cli.CommandLine;
13+
import org.apache.commons.cli.ParseException;
14+
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
18+
public class AddCallbackUrl implements ITinker {
19+
20+
/**
21+
* Notification filter constants.
22+
*/
23+
private static final String NOTIFICATION_DELIVERY_METHOD_URL = "URL";
24+
private static final String NOTIFICATION_CATEGORY_MUTATION = "MUTATION";
25+
26+
/**
27+
* Error constants.
28+
*/
29+
private static final String ERROR_UNKNOWN_USER_TYPE_RETURNED = "Unknown user type returned.";
30+
private static final String ERROR_CANNOT_UPDATE_NOTIFICATION_FILTER_FOR_USER_LIGHT = "Unknown user type returned.";
31+
32+
/**
33+
* @param args
34+
*
35+
* @throws ParseException
36+
*/
37+
public void run(String[] args) throws ParseException {
38+
CommandLine allOption = SharedLib.parseAllOption(args);
39+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
40+
41+
SharedLib.printHeader();
42+
43+
BunqLib bunq = new BunqLib(environmentType);
44+
45+
String callbackUrl = SharedLib.determineCallbackUrlFromAllOptionOrStdIn(allOption);
46+
47+
System.out.println();
48+
System.out.println(" | Adding Callback URL: " + callbackUrl);
49+
System.out.println();
50+
System.out.println(" ...");
51+
System.out.println();
52+
53+
List<NotificationFilter> allNotificationFilterCurrent;
54+
List<NotificationFilter> allNotificationFilterUpdated = new ArrayList<>();
55+
56+
if (bunq.getUser().getReferencedObject() instanceof UserPerson) {
57+
allNotificationFilterCurrent = bunq.getUser().getUserPerson().getNotificationFilters();
58+
} else if (bunq.getUser().getReferencedObject() instanceof UserCompany) {
59+
allNotificationFilterCurrent = bunq.getUser().getUserCompany().getNotificationFilters();
60+
} else if (bunq.getUser().getReferencedObject() instanceof UserLight) {
61+
allNotificationFilterCurrent = bunq.getUser().getUserLight().getNotificationFilters();
62+
} else {
63+
throw new BunqException(ERROR_UNKNOWN_USER_TYPE_RETURNED);
64+
}
65+
66+
for (NotificationFilter notificationFilter : allNotificationFilterCurrent) {
67+
if (callbackUrl.equals(notificationFilter.getNotificationTarget())) {
68+
allNotificationFilterUpdated.add(notificationFilter);
69+
}
70+
}
71+
72+
allNotificationFilterUpdated.add(
73+
new NotificationFilter(NOTIFICATION_DELIVERY_METHOD_URL, callbackUrl, NOTIFICATION_CATEGORY_MUTATION)
74+
);
75+
76+
if (bunq.getUser().getReferencedObject() instanceof UserPerson) {
77+
UserPerson.update(
78+
null, /* firstName */
79+
null, /* middleName */
80+
null, /* lastName */
81+
null, /* publicNickName */
82+
null, /* addressMain */
83+
null, /* addressPostal */
84+
null, /* avatarUuid */
85+
null, /* taxResident */
86+
null, /* documentType */
87+
null, /* documentNumber */
88+
null, /* documentCountryOfIssuance */
89+
null, /* documentFrontAttachmentId */
90+
null, /* documentBackAttachmentId */
91+
null, /* dataOfBirth */
92+
null, /* placeOfBirth */
93+
null, /* countryOfBirth */
94+
null, /* nationality */
95+
null, /* language */
96+
null, /* region */
97+
null, /* gender */
98+
null, /* status */
99+
null, /* subStatus */
100+
null, /* legalGuardianAlias */
101+
null, /* sessionTimeout */
102+
null, /* CardIds */
103+
null, /* cardLimits */
104+
null, /* dailyLimitWithoutConfirmationLogin */
105+
allNotificationFilterUpdated
106+
);
107+
} else if (bunq.getUser().getReferencedObject() instanceof UserCompany) {
108+
UserCompany.update(
109+
null, /* name */
110+
null, /* publicNickName */
111+
null, /* avatarUuid */
112+
null, /* addressMain */
113+
null, /* addressPostal */
114+
null, /* language */
115+
null, /* region */
116+
null, /* country */
117+
null, /* ubo */
118+
null, /* chamberOfCommerce */
119+
null, /* status */
120+
null, /* subStatus */
121+
null, /* sessionTimeout */
122+
null, /* dailyLimitWithoutConfirmationLogin */
123+
allNotificationFilterUpdated
124+
);
125+
} else if (bunq.getUser().getReferencedObject() instanceof UserLight) {
126+
throw new BunqException(ERROR_CANNOT_UPDATE_NOTIFICATION_FILTER_FOR_USER_LIGHT);
127+
} else {
128+
throw new BunqException(ERROR_UNKNOWN_USER_TYPE_RETURNED);
129+
}
130+
131+
System.out.println();
132+
System.out.println(" | ✅ Callback URL added");
133+
System.out.println();
134+
System.out.println(" | ▶️ Check your changed overview");
135+
System.out.println();
136+
System.out.println();
137+
138+
bunq.updateContext();
139+
}
140+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiContext;
4+
import com.bunq.sdk.context.ApiEnvironmentType;
5+
import com.bunq.sdk.exception.BunqException;
6+
import com.bunq.tinker.utils.ITinker;
7+
import org.apache.commons.cli.*;
8+
9+
import java.net.InetAddress;
10+
import java.net.UnknownHostException;
11+
12+
public class CreateProductionConfiguration implements ITinker {
13+
14+
/**
15+
*
16+
* @param args
17+
* @throws ParseException
18+
*/
19+
public void run(String[] args) throws ParseException, UnknownHostException {
20+
Options options = new Options();
21+
options.addOption(new Option("", "api-key", true, ""));
22+
CommandLineParser parser = new BasicParser();
23+
24+
CommandLine allOption = parser.parse(options, args);
25+
26+
if (allOption.hasOption("api-key")) {
27+
ApiContext.create(
28+
ApiEnvironmentType.PRODUCTION,
29+
allOption.getOptionValue("api-key"),
30+
InetAddress.getLocalHost().getHostName()
31+
).save("bunq-production.conf");
32+
} else {
33+
throw new BunqException("Missing mandatory option \"--api-key [API key]\"");
34+
}
35+
36+
}
37+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.model.generated.endpoint.Card;
5+
import com.bunq.tinker.libs.BunqLib;
6+
import com.bunq.tinker.libs.SharedLib;
7+
import com.bunq.tinker.utils.ITinker;
8+
import org.apache.commons.cli.CommandLine;
9+
import org.apache.commons.cli.ParseException;
10+
11+
public class LinkCard implements ITinker {
12+
13+
/**
14+
* @param args
15+
*
16+
* @throws ParseException
17+
*/
18+
public void run(String[] args) throws ParseException {
19+
CommandLine allOption = SharedLib.parseAllOption(args);
20+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
21+
22+
SharedLib.printHeader();
23+
24+
BunqLib bunq = new BunqLib(environmentType);
25+
26+
String cardId = SharedLib.determineCardIdFromAllOptionOrStdIn(allOption);
27+
String accountId = SharedLib.determineAccountIdFromAllOptionOrStdIn(allOption);
28+
29+
System.out.println();
30+
System.out.println(" | Link Card: " + cardId);
31+
System.out.println(" | To Account: " + accountId);
32+
System.out.println();
33+
System.out.println(" ...");
34+
System.out.println();
35+
36+
Card.update(
37+
Integer.parseInt(cardId),
38+
null, /* pinCode */
39+
null, /* activationCode */
40+
null, /* status */
41+
null, /* limit */
42+
null, /* magStripePermissions */
43+
null, /* countryPermission */
44+
Integer.parseInt(accountId)
45+
);
46+
47+
System.out.println();
48+
System.out.println(" | ✅ Account switched");
49+
System.out.println();
50+
System.out.println(" | ▶️ Check your changed overview");
51+
System.out.println();
52+
System.out.println();
53+
54+
bunq.updateContext();
55+
}
56+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.model.generated.endpoint.Payment;
5+
import com.bunq.sdk.model.generated.object.Amount;
6+
import com.bunq.sdk.model.generated.object.Pointer;
7+
import com.bunq.tinker.libs.BunqLib;
8+
import com.bunq.tinker.libs.SharedLib;
9+
import com.bunq.tinker.utils.ITinker;
10+
import org.apache.commons.cli.CommandLine;
11+
import org.apache.commons.cli.ParseException;
12+
13+
public class MakePayment implements ITinker {
14+
15+
/**
16+
* Input constants.
17+
*/
18+
private static final String CURRENCY_EURO = "EUR";
19+
private static final String POINTER_TYPE_EMAIL = "EMAIL";
20+
21+
/**
22+
* @param args
23+
*
24+
* @throws ParseException
25+
*/
26+
public void run(String[] args) throws ParseException {
27+
CommandLine allOption = SharedLib.parseAllOption(args);
28+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
29+
30+
SharedLib.printHeader();
31+
32+
BunqLib bunq = new BunqLib(environmentType);
33+
34+
String amount = SharedLib.determineAmountFromAllOptionOrStdIn(allOption);
35+
String description = SharedLib.determineDescriptionFromAllOptionOrStdIn(allOption);
36+
String recipient = SharedLib.determineRecipientFromAllOptionOrStdIn(allOption);
37+
38+
System.out.println();
39+
System.out.println(" | Sending: € " + amount);
40+
System.out.println(" | To: " + recipient);
41+
System.out.println(" | Description: " + description);
42+
System.out.println();
43+
System.out.println(" ...");
44+
System.out.println();
45+
46+
Payment.create(new Amount(amount, CURRENCY_EURO), new Pointer(POINTER_TYPE_EMAIL, recipient), description);
47+
48+
System.out.println();
49+
System.out.println(" | ✅ Payment sent");
50+
System.out.println();
51+
System.out.println(" | ▶️ Check your changed overview");
52+
System.out.println();
53+
System.out.println();
54+
55+
bunq.updateContext();
56+
}
57+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.model.generated.endpoint.RequestInquiry;
5+
import com.bunq.sdk.model.generated.object.Amount;
6+
import com.bunq.sdk.model.generated.object.Pointer;
7+
import com.bunq.tinker.libs.BunqLib;
8+
import com.bunq.tinker.libs.SharedLib;
9+
import com.bunq.tinker.utils.ITinker;
10+
import org.apache.commons.cli.CommandLine;
11+
import org.apache.commons.cli.ParseException;
12+
13+
public class MakeRequest implements ITinker {
14+
15+
/**
16+
* Input constants.
17+
*/
18+
private static final String CURRENCY_EURO = "EUR";
19+
private static final String POINTER_TYPE_EMAIL = "EMAIL";
20+
21+
/**
22+
* @param args
23+
*
24+
* @throws ParseException
25+
*/
26+
public void run(String[] args) throws ParseException {
27+
CommandLine allOption = SharedLib.parseAllOption(args);
28+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
29+
30+
SharedLib.printHeader();
31+
32+
BunqLib bunq = new BunqLib(environmentType);
33+
34+
String amount = SharedLib.determineAmountFromAllOptionOrStdIn(allOption);
35+
String description = SharedLib.determineDescriptionFromAllOptionOrStdIn(allOption);
36+
String recipient = SharedLib.determineRecipientFromAllOptionOrStdIn(allOption);
37+
38+
System.out.println();
39+
System.out.println(" | Requesting: € " + amount);
40+
System.out.println(" | From: " + recipient);
41+
System.out.println(" | Description: " + description);
42+
System.out.println();
43+
System.out.println(" ...");
44+
System.out.println();
45+
46+
RequestInquiry.create(
47+
new Amount(amount, CURRENCY_EURO),
48+
new Pointer(POINTER_TYPE_EMAIL, recipient),
49+
description,
50+
true
51+
);
52+
53+
System.out.println();
54+
System.out.println(" | ✅ Request sent");
55+
System.out.println();
56+
System.out.println(" | ▶️ Check your changed overview");
57+
System.out.println();
58+
System.out.println();
59+
60+
bunq.updateContext();
61+
}
62+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.model.generated.endpoint.MonetaryAccountBank;
5+
import com.bunq.tinker.libs.BunqLib;
6+
import com.bunq.tinker.libs.SharedLib;
7+
import com.bunq.tinker.utils.ITinker;
8+
import org.apache.commons.cli.CommandLine;
9+
import org.apache.commons.cli.ParseException;
10+
11+
public class UpdateAccount implements ITinker {
12+
13+
/**
14+
* @param args
15+
*
16+
* @throws ParseException
17+
*/
18+
public void run(String[] args) throws ParseException {
19+
CommandLine allOption = SharedLib.parseAllOption(args);
20+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
21+
22+
SharedLib.printHeader();
23+
24+
BunqLib bunq = new BunqLib(environmentType);
25+
26+
String name = SharedLib.determineNameFromAllOptionOrStdIn(allOption);
27+
String accountId = SharedLib.determineAccountIdFromAllOptionOrStdIn(allOption);
28+
29+
System.out.println();
30+
System.out.println(" | Updating Name: " + name);
31+
System.out.println(" | of Account: " + accountId);
32+
System.out.println();
33+
System.out.println(" ...");
34+
System.out.println();
35+
36+
MonetaryAccountBank.update(Integer.parseInt(accountId), name);
37+
System.out.println();
38+
System.out.println(" | ✅ Account updated");
39+
System.out.println();
40+
System.out.println(" | ▶️ Check your changed overview");
41+
System.out.println();
42+
System.out.println();
43+
44+
bunq.updateContext();
45+
}
46+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.bunq.tinker;
2+
3+
import com.bunq.sdk.context.ApiEnvironmentType;
4+
import com.bunq.sdk.model.generated.endpoint.*;
5+
import com.bunq.sdk.model.generated.object.Pointer;
6+
import com.bunq.tinker.libs.BunqLib;
7+
import com.bunq.tinker.libs.SharedLib;
8+
import com.bunq.tinker.utils.ITinker;
9+
import org.apache.commons.cli.*;
10+
11+
import java.util.List;
12+
13+
public class UserOverview implements ITinker {
14+
15+
/**
16+
* @param args
17+
*
18+
* @throws ParseException
19+
*/
20+
public void run(String[] args) throws ParseException {
21+
CommandLine allOption = SharedLib.parseAllOption(args);
22+
ApiEnvironmentType environmentType = SharedLib.determineEnvironmentType(allOption);
23+
24+
SharedLib.printHeader();
25+
26+
BunqLib bunq = new BunqLib(environmentType);
27+
28+
User user = bunq.getUser();
29+
SharedLib.printUser(user);
30+
31+
List<MonetaryAccountBank> allMonetaryAccountBankActive = bunq.getAllMonetaryAccountBankActive(1);
32+
SharedLib.printAllMonetaryAccountBank(allMonetaryAccountBankActive);
33+
34+
List<Payment> allPayment = bunq.getAllPayment(allMonetaryAccountBankActive.get(0), 1);
35+
SharedLib.printAllPayment(allPayment);
36+
37+
List<RequestInquiry> allRequest = bunq.getAllRequest(allMonetaryAccountBankActive.get(0), 1);
38+
SharedLib.printAllRequest(allRequest);
39+
40+
List<Card> allCard = bunq.getAllCard(1);
41+
SharedLib.printAllCard(allCard, allMonetaryAccountBankActive);
42+
43+
List<Pointer> allUserAlias = bunq.getAllUserAlias();
44+
SharedLib.printAllUserAlias(allUserAlias);
45+
46+
System.out.println("");
47+
System.out.println("");
48+
System.out.println(" Want to see more monetary accounts, payments, requests or even cards?");
49+
System.out.println(" Adjust this file.");
50+
System.out.println("");
51+
System.out.println("");
52+
System.out.println("");
53+
54+
bunq.updateContext();
55+
}
56+
}
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
package com.bunq.tinker.libs;
2+
3+
import com.bunq.sdk.context.ApiContext;
4+
import com.bunq.sdk.context.ApiEnvironmentType;
5+
import com.bunq.sdk.context.BunqContext;
6+
import com.bunq.sdk.exception.BunqException;
7+
import com.bunq.sdk.http.Pagination;
8+
import com.bunq.sdk.model.generated.endpoint.Card;
9+
import com.bunq.sdk.model.generated.endpoint.MonetaryAccountBank;
10+
import com.bunq.sdk.model.generated.endpoint.Payment;
11+
import com.bunq.sdk.model.generated.endpoint.RequestInquiry;
12+
import com.bunq.sdk.model.generated.endpoint.SandboxUser;
13+
import com.bunq.sdk.model.generated.endpoint.User;
14+
import com.bunq.sdk.model.generated.endpoint.UserCompany;
15+
import com.bunq.sdk.model.generated.endpoint.UserPerson;
16+
import com.bunq.sdk.model.generated.object.LabelMonetaryAccount;
17+
import com.bunq.sdk.model.generated.object.Pointer;
18+
import com.google.gson.Gson;
19+
import com.google.gson.JsonObject;
20+
import com.google.gson.stream.JsonReader;
21+
import okhttp3.OkHttpClient;
22+
import okhttp3.Request;
23+
import okhttp3.RequestBody;
24+
import okhttp3.Response;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.io.StringReader;
29+
import java.util.ArrayList;
30+
import java.util.List;
31+
32+
public class BunqLib {
33+
34+
private static final int DEFAULT_FETCH_COUNT = 10;
35+
36+
/**
37+
* Error constants.
38+
*/
39+
private static final String ERROR_COULD_NOT_FIND_ALIAS_TYPE_IBAN = "Could not find alias with type IBAN for monetary account \"%s\"";
40+
private static final String ERROR_COULD_NOT_GENERATE_NEW_API_KEY = "Encountered error while retrieving new sandbox ApiKey.\nError message %s";
41+
private static final String ERROR_COULD_NOT_FIND_CONFIG_FILE = "Could not find a bunq configuration to load.";
42+
43+
/**
44+
* FileName constatns.
45+
*/
46+
private static final String FILE_NAME_BUNQ_CONF_PRODUCTION = "bunq-production.conf";
47+
private static final String FILE_NAME_BUNQ_CONF_SANDBOX = "bunq-sandbox.conf";
48+
49+
/**
50+
* Field constatns.
51+
*/
52+
private static final String FIELD_RESPONSE = "Response";
53+
private static final String FIELD_API_KEY = "ApiKey";
54+
55+
/**
56+
*/
57+
private static final String POINTER_TYPE_IBAN = "IBAN";
58+
private static final String MONETARY_ACCOUNT_STATUS_ACTIVE = "ACTIVE";
59+
private static final String DEVICE_SERVER_DESCRIPTION = "bunq Tinker java";
60+
61+
/**
62+
* The index of the fist item in an array.
63+
*/
64+
private static final int INDEX_FIRST = 0;
65+
66+
/**
67+
* Http constatns.
68+
*/
69+
private static final int HTTP_STATUS_OK = 200;
70+
public static final String ERROR_COULD_NOT_DETERMINE_USER_TYPE = "Could not determine user type";
71+
72+
private ApiEnvironmentType environmentType;
73+
74+
private User user;
75+
76+
public BunqLib(ApiEnvironmentType environmentType) {
77+
this.environmentType = environmentType;
78+
79+
this.setupContext();
80+
this.setupCurrentUser();
81+
}
82+
83+
/**
84+
*
85+
*/
86+
private void setupContext() {
87+
if (new File(this.determineBunqConfigFileName()).exists()) {
88+
// Config is already present.
89+
} else if (ApiEnvironmentType.SANDBOX.equals(this.environmentType)) {
90+
SandboxUser sandboxUser = generateNewSandboxUser();
91+
ApiContext.create(ApiEnvironmentType.SANDBOX, sandboxUser.getApiKey(), DEVICE_SERVER_DESCRIPTION).save(this.determineBunqConfigFileName());
92+
} else {
93+
throw new BunqException(ERROR_COULD_NOT_FIND_CONFIG_FILE);
94+
}
95+
96+
ApiContext apiContext = ApiContext.restore(this.determineBunqConfigFileName());
97+
98+
apiContext.ensureSessionActive();
99+
apiContext.save(this.determineBunqConfigFileName());
100+
101+
BunqContext.loadApiContext(apiContext);
102+
}
103+
104+
public void updateContext() {
105+
BunqContext.getApiContext().save(this.determineBunqConfigFileName());
106+
}
107+
108+
/**
109+
*
110+
* @return String
111+
*/
112+
private String determineBunqConfigFileName() {
113+
if (ApiEnvironmentType.PRODUCTION.equals(this.environmentType)) {
114+
return FILE_NAME_BUNQ_CONF_PRODUCTION;
115+
} else {
116+
return FILE_NAME_BUNQ_CONF_SANDBOX;
117+
}
118+
}
119+
120+
/**
121+
*
122+
*/
123+
private void setupCurrentUser() {
124+
this.user = User.get().getValue();
125+
}
126+
127+
public User getUser() {
128+
return this.user;
129+
}
130+
131+
public List<MonetaryAccountBank> getAllMonetaryAccountBankActive() {
132+
return getAllMonetaryAccountBankActive(DEFAULT_FETCH_COUNT);
133+
}
134+
135+
public List<MonetaryAccountBank> getAllMonetaryAccountBankActive(int count) {
136+
Pagination pagination = new Pagination();
137+
pagination.setCount(count);
138+
139+
List<MonetaryAccountBank> allAccount = MonetaryAccountBank.list(pagination.getUrlParamsCountOnly()).getValue();
140+
List<MonetaryAccountBank> allAccountActive = new ArrayList<>();
141+
142+
for (MonetaryAccountBank account : allAccount) {
143+
if (account.getStatus().equals(MONETARY_ACCOUNT_STATUS_ACTIVE)) {
144+
allAccountActive.add(account);
145+
} else {
146+
// Account is not active.
147+
}
148+
}
149+
150+
return allAccountActive;
151+
}
152+
153+
public List<Payment> getAllPayment(MonetaryAccountBank monetaryAccountBank) {
154+
return getAllPayment(monetaryAccountBank, DEFAULT_FETCH_COUNT);
155+
}
156+
157+
public List<Payment> getAllPayment(MonetaryAccountBank monetaryAccountBank, int count) {
158+
Pagination pagination = new Pagination();
159+
pagination.setCount(count);
160+
161+
return Payment.list(
162+
monetaryAccountBank.getId(),
163+
pagination.getUrlParamsCountOnly()
164+
).getValue();
165+
}
166+
167+
public List<RequestInquiry> getAllRequest(MonetaryAccountBank monetaryAccountBank) {
168+
return getAllRequest(monetaryAccountBank, DEFAULT_FETCH_COUNT);
169+
}
170+
171+
public List<RequestInquiry> getAllRequest(MonetaryAccountBank monetaryAccountBank, int count) {
172+
Pagination pagination = new Pagination();
173+
pagination.setCount(count);
174+
175+
return RequestInquiry.list(
176+
monetaryAccountBank.getId(),
177+
pagination.getUrlParamsCountOnly()
178+
).getValue();
179+
}
180+
181+
public List<Card> getAllCard() {
182+
return getAllCard(DEFAULT_FETCH_COUNT);
183+
}
184+
185+
public List<Card> getAllCard(int count) {
186+
Pagination pagination = new Pagination();
187+
pagination.setCount(count);
188+
189+
return Card.list(
190+
pagination.getUrlParamsCountOnly()
191+
).getValue();
192+
}
193+
194+
public static Pointer getPointerIbanForMonetaryAccountBank(MonetaryAccountBank monetaryAccountBank) {
195+
for (Pointer pointer : monetaryAccountBank.getAlias()) {
196+
if (pointer.getType().equals(POINTER_TYPE_IBAN)) {
197+
return pointer;
198+
}
199+
}
200+
201+
throw new BunqException(
202+
String.format(
203+
ERROR_COULD_NOT_FIND_ALIAS_TYPE_IBAN,
204+
monetaryAccountBank.getDescription()
205+
)
206+
);
207+
}
208+
209+
public static MonetaryAccountBank getMonetaryAccountBankFromLabel(
210+
LabelMonetaryAccount label,
211+
List<MonetaryAccountBank> allMonetaryAccountBank
212+
) {
213+
String labelIban = label.getIban();
214+
215+
for (MonetaryAccountBank monetaryAccountBank : allMonetaryAccountBank) {
216+
String monetaryAccountBankIban = getPointerIbanForMonetaryAccountBank(monetaryAccountBank).getValue();
217+
218+
if (labelIban.equals(monetaryAccountBankIban)) {
219+
return monetaryAccountBank;
220+
} else {
221+
// Have not found the matching MonetaryAccountBank yet.
222+
}
223+
}
224+
225+
return null;
226+
}
227+
228+
public List<Pointer> getAllUserAlias() {
229+
if (this.getUser().getReferencedObject() instanceof UserPerson) {
230+
return ((UserPerson) this.getUser().getReferencedObject()).getAlias();
231+
} else if (this.getUser().getReferencedObject() instanceof UserCompany) {
232+
return ((UserCompany) this.getUser().getReferencedObject()).getAlias();
233+
} else {
234+
throw new BunqException(ERROR_COULD_NOT_DETERMINE_USER_TYPE);
235+
}
236+
}
237+
238+
private SandboxUser generateNewSandboxUser() {
239+
OkHttpClient client = new OkHttpClient();
240+
241+
Request request = new Request.Builder()
242+
.url("https://sandbox.public.api.bunq.com/v1/sandbox-user")
243+
.post(RequestBody.create(null, new byte[0]))
244+
.addHeader("x-bunq-client-request-id", "1234")
245+
.addHeader("cache-control", "no-cache")
246+
.addHeader("x-bunq-geolocation", "0 0 0 0 NL")
247+
.addHeader("x-bunq-language", "en_US")
248+
.addHeader("x-bunq-region", "en_US")
249+
.build();
250+
251+
try {
252+
Response response = client.newCall(request).execute();
253+
if (response.code() == HTTP_STATUS_OK) {
254+
String responseString = response.body().string();
255+
JsonObject jsonObject = new Gson().fromJson(responseString, JsonObject.class);
256+
JsonObject apiKEy = jsonObject.getAsJsonArray(FIELD_RESPONSE).get(INDEX_FIRST).getAsJsonObject().get(FIELD_API_KEY).getAsJsonObject();
257+
258+
return SandboxUser.fromJsonReader(new JsonReader(new StringReader(apiKEy.toString())));
259+
} else {
260+
throw new BunqException(String.format(ERROR_COULD_NOT_GENERATE_NEW_API_KEY, response.body().string()));
261+
}
262+
} catch (IOException e) {
263+
throw new BunqException(e.getMessage());
264+
}
265+
}
266+
}

‎src/main/java/com/bunq/tinker/libs/SharedLib.java

Lines changed: 318 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.bunq.tinker.utils;
2+
3+
public interface ITinker {
4+
void run(String[] args) throws Exception;
5+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.bunq.tinker.utils;
2+
3+
public class TinkerRunner {
4+
5+
/**
6+
* Package constants.
7+
*/
8+
public static final String PACKAGE_TINKER_PREFIX = "com.bunq.tinker.";
9+
10+
/**
11+
*
12+
* @param args
13+
*/
14+
public static void main(String[] args) throws Exception {
15+
if (args.length <= 0) return;
16+
17+
String className = args[0];
18+
Class classObject = Class.forName(PACKAGE_TINKER_PREFIX + className);
19+
ITinker tinker = (ITinker) classObject.newInstance();
20+
21+
tinker.run(args);
22+
}
23+
}

0 commit comments

Comments
 (0)
Please sign in to comment.