From fc33b5021a23a4fe385d390fff5a5f132b77f459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:47:07 +0200 Subject: [PATCH 001/118] First (and wildly buggy) stab at payment processor --- payment-processor/build.gradle | 12 ++++ payment-processor/doc/generate-diagrams.sh | 3 + payment-processor/doc/puml/signin-flow.puml | 9 +++ .../paymentprocessor/FirebaseAppNotifier.kt | 69 +++++++++++++++++++ .../prime/paymentprocessor/FirebaseModule.kt | 64 +++++++++++++++++ .../io.dropwizard.jackson.Discoverable | 1 + .../org.ostelco.prime.appnotifier.AppNotifier | 1 + .../org.ostelco.prime.module.PrimeModule | 1 + 8 files changed, 160 insertions(+) create mode 100644 payment-processor/build.gradle create mode 100755 payment-processor/doc/generate-diagrams.sh create mode 100644 payment-processor/doc/puml/signin-flow.puml create mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt create mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt create mode 100644 payment-processor/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable create mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier create mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule diff --git a/payment-processor/build.gradle b/payment-processor/build.gradle new file mode 100644 index 000000000..e6a8e3267 --- /dev/null +++ b/payment-processor/build.gradle @@ -0,0 +1,12 @@ +plugins { + id "org.jetbrains.kotlin.jvm" version "1.2.50" + id "java-library" +} + +dependencies { + implementation project(":prime-api") + // Keep it to 5.10.0 to match netty via ocs-api + implementation 'com.google.firebase:firebase-admin:6.2.0' + + testImplementation "junit:junit:4.12" +} \ No newline at end of file diff --git a/payment-processor/doc/generate-diagrams.sh b/payment-processor/doc/generate-diagrams.sh new file mode 100755 index 000000000..f3fae4224 --- /dev/null +++ b/payment-processor/doc/generate-diagrams.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +plantuml -tsvg -pipe < puml/signin-flow.puml > diagrams/signin-flow.svg diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/signin-flow.puml new file mode 100644 index 000000000..9fe4e0c8a --- /dev/null +++ b/payment-processor/doc/puml/signin-flow.puml @@ -0,0 +1,9 @@ +@startuml + +Client -> Auth0 : Auth +Auth0 -> Client: Tokens (ID (with email), Access) +Client -> CloudEP: GET /profile (https, with access token) +CloudEP -> Prime: GET /profile (http, with access token) +Prime -> Client: User profile (email, address, etc.) + +@enduml diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt new file mode 100644 index 000000000..70e637207 --- /dev/null +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt @@ -0,0 +1,69 @@ +package org.ostelco.prime.appnotifier + +import com.google.api.core.ApiFutureCallback +import com.google.api.core.ApiFutures.addCallback +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import com.google.firebase.messaging.FirebaseMessaging +import com.google.firebase.messaging.Message +import com.google.firebase.messaging.Notification +import org.ostelco.prime.module.getResource +import org.ostelco.prime.storage.legacy.Storage +import java.io.FileInputStream +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths + +class FirebaseAppNotifier: AppNotifier { + + val listOfFailureCodes = listOf( + "messaging/invalid-recipient", + "messaging/invalid-registration-token", + "messaging/registration-token-not-registered" + ) + + override fun notify(msisdn: String, title: String, body: String) { + println("Will try to notify msisdn : $msisdn") + sendNotification(msisdn, title, body) + } + + private fun sendNotification(msisdn: String, title: String, body: String) { + + val store = getResource() + + // This registration token comes from the client FCM SDKs. + val applicationTokens = store.getNotificationTokens(msisdn) + + for (applicationToken in applicationTokens) { + + if (applicationToken.tokenType.equals("FCM")) { + // See documentation on defining a message payload. + val message = Message.builder() + .setNotification(Notification(title, body)) + .setToken(applicationToken.token) + .build() + + // Send a message to the device corresponding to the provided + // registration token. + val future = FirebaseMessaging + .getInstance(FirebaseApp.getInstance("fcm")) + .sendAsync(message) + + val apiFutureCallback = object : ApiFutureCallback { + override fun onSuccess(result: String) { + println("Notification completed with result: $result") + if (listOfFailureCodes.contains(result)) { + store.removeNotificationToken(msisdn, applicationToken.applicationID) + } + } + + override fun onFailure(t: Throwable) { + println("Notification failed with error: $t") + } + } + addCallback(future, apiFutureCallback) + } + } + } +} \ No newline at end of file diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt new file mode 100644 index 000000000..da4a2d12c --- /dev/null +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt @@ -0,0 +1,64 @@ +package org.ostelco.prime.appnotifier + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonTypeName +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import org.hibernate.validator.constraints.NotEmpty +import org.ostelco.prime.module.PrimeModule +import java.io.FileInputStream +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths + +@JsonTypeName("firebase-app-notifier") +class FirebaseModule : PrimeModule { + + @JsonProperty("config") + fun setConfig(config: FirebaseConfig) { + println("Config set for AppNotifier") + setupFirebaseApp(config.databaseName, config.configFile) + } + + private fun setupFirebaseApp( + databaseName: String, + configFile: String) { + + try { + println("Setting up Firebase for FirebaseAppNotifier. databaseName : $databaseName , configFile : $configFile ") + val credentials: GoogleCredentials = if (Files.exists(Paths.get(configFile))) { + FileInputStream(configFile).use { serviceAccount -> GoogleCredentials.fromStream(serviceAccount) } + } else { + GoogleCredentials.getApplicationDefault() + } + + val options = FirebaseOptions.Builder() + .setCredentials(credentials) + .setDatabaseUrl("https://$databaseName.firebaseio.com/") + .build() + try { + FirebaseApp.getInstance("fcm") + } catch (e: Exception) { + FirebaseApp.initializeApp(options, "fcm") + } + + // (un)comment next line to turn on/of extended debugging + // from firebase. + // this.firebaseDatabase.setLogLevel(com.google.firebase.database.Logger.Level.DEBUG); + } catch (ex: IOException) { + throw AppNotifierException(ex) + } + } +} + +class FirebaseConfig { + + @NotEmpty + @JsonProperty("databaseName") + lateinit var databaseName: String + + @NotEmpty + @JsonProperty("configFile") + lateinit var configFile: String +} \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable b/payment-processor/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable new file mode 100644 index 000000000..8056fe23b --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable @@ -0,0 +1 @@ +org.ostelco.prime.module.PrimeModule \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier new file mode 100644 index 000000000..a8cc68ad4 --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier @@ -0,0 +1 @@ +org.ostelco.prime.appnotifier.FirebaseAppNotifier \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule new file mode 100644 index 000000000..40b4ef01e --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule @@ -0,0 +1 @@ +org.ostelco.prime.appnotifier.FirebaseModule \ No newline at end of file From cb1570e8448d5ce30aeb6dc4810a1521cd320376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:51:58 +0200 Subject: [PATCH 002/118] Put in the placeholder diagram, not correct, but something that can be corrected. --- payment-processor/doc/generate-diagrams.sh | 2 +- payment-processor/doc/puml/signin-flow.puml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/payment-processor/doc/generate-diagrams.sh b/payment-processor/doc/generate-diagrams.sh index f3fae4224..3d052437c 100755 --- a/payment-processor/doc/generate-diagrams.sh +++ b/payment-processor/doc/generate-diagrams.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -plantuml -tsvg -pipe < puml/signin-flow.puml > diagrams/signin-flow.svg +nplantuml -tsvg -pipe < puml/purchase-flow.puml > diagrams/purchase-flow.svg diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/signin-flow.puml index 9fe4e0c8a..2b3a45d91 100644 --- a/payment-processor/doc/puml/signin-flow.puml +++ b/payment-processor/doc/puml/signin-flow.puml @@ -1,9 +1,10 @@ @startuml -Client -> Auth0 : Auth -Auth0 -> Client: Tokens (ID (with email), Access) -Client -> CloudEP: GET /profile (https, with access token) -CloudEP -> Prime: GET /profile (http, with access token) -Prime -> Client: User profile (email, address, etc.) +Handset -> Stripe : CC-info +Stripe -> Handset: PaymentToken +Handset -> Prime: Buy(prod, paymentToken, etc.) +Prime -> Stripe: Buy(token, ...) +Stripe -> Prime: Ok +Prime -> Handset: Response of buy thingy @enduml From 45cbc0c5ae65abb51466dd694f703aef3206025e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:53:44 +0200 Subject: [PATCH 003/118] Placeholder diagram and readme file --- payment-processor/README.md | 2 ++ .../doc/diagrams/signin-flow.svg | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 payment-processor/README.md create mode 100644 payment-processor/doc/diagrams/signin-flow.svg diff --git a/payment-processor/README.md b/payment-processor/README.md new file mode 100644 index 000000000..80cd37d4f --- /dev/null +++ b/payment-processor/README.md @@ -0,0 +1,2 @@ +Placeholder for documentation for the payment processor + diff --git a/payment-processor/doc/diagrams/signin-flow.svg b/payment-processor/doc/diagrams/signin-flow.svg new file mode 100644 index 000000000..7384fa360 --- /dev/null +++ b/payment-processor/doc/diagrams/signin-flow.svg @@ -0,0 +1,23 @@ +HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy \ No newline at end of file From 0f5770017e1e9295eba00bd3a7ec7d1b6ccf7614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 17:21:03 +0200 Subject: [PATCH 004/118] clarify dummy diagram --- payment-processor/doc/generate-diagrams.sh | 2 +- .../doc/puml/{signin-flow.puml => purchase-flow.puml} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename payment-processor/doc/puml/{signin-flow.puml => purchase-flow.puml} (50%) diff --git a/payment-processor/doc/generate-diagrams.sh b/payment-processor/doc/generate-diagrams.sh index 3d052437c..405ebfa24 100755 --- a/payment-processor/doc/generate-diagrams.sh +++ b/payment-processor/doc/generate-diagrams.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -nplantuml -tsvg -pipe < puml/purchase-flow.puml > diagrams/purchase-flow.svg +plantuml -tsvg -pipe < puml/purchase-flow.puml > diagrams/purchase-flow.svg diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/purchase-flow.puml similarity index 50% rename from payment-processor/doc/puml/signin-flow.puml rename to payment-processor/doc/puml/purchase-flow.puml index 2b3a45d91..858761ab0 100644 --- a/payment-processor/doc/puml/signin-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -2,9 +2,9 @@ Handset -> Stripe : CC-info Stripe -> Handset: PaymentToken -Handset -> Prime: Buy(prod, paymentToken, etc.) +Handset --> Prime: Buy(prod, paymentToken, etc.) Prime -> Stripe: Buy(token, ...) -Stripe -> Prime: Ok -Prime -> Handset: Response of buy thingy +Stripe --> Prime: Ok +Prime ---> Handset: Response of buy thingy @enduml From 1c7c5335563228fd8254d00e1147eb5833bb7ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 17:21:17 +0200 Subject: [PATCH 005/118] clarify dummy diagram --- .../doc/diagrams/purchase-flow.svg | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 payment-processor/doc/diagrams/purchase-flow.svg diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg new file mode 100644 index 000000000..7e04bf9e2 --- /dev/null +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -0,0 +1,23 @@ +HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy \ No newline at end of file From 91695a2be81f6a0104ccc2414dc5a77c9fd34e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 17:21:50 +0200 Subject: [PATCH 006/118] Strip away scaffolding to make this thing clearer. --- .../paymentprocessor/FirebaseAppNotifier.kt | 69 ------------------- .../prime/paymentprocessor/FirebaseModule.kt | 44 +++--------- .../org.ostelco.prime.appnotifier.AppNotifier | 1 - .../org.ostelco.prime.module.PrimeModule | 1 - 4 files changed, 8 insertions(+), 107 deletions(-) delete mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt delete mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier delete mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt deleted file mode 100644 index 70e637207..000000000 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt +++ /dev/null @@ -1,69 +0,0 @@ -package org.ostelco.prime.appnotifier - -import com.google.api.core.ApiFutureCallback -import com.google.api.core.ApiFutures.addCallback -import com.google.auth.oauth2.GoogleCredentials -import com.google.firebase.FirebaseApp -import com.google.firebase.FirebaseOptions -import com.google.firebase.messaging.FirebaseMessaging -import com.google.firebase.messaging.Message -import com.google.firebase.messaging.Notification -import org.ostelco.prime.module.getResource -import org.ostelco.prime.storage.legacy.Storage -import java.io.FileInputStream -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths - -class FirebaseAppNotifier: AppNotifier { - - val listOfFailureCodes = listOf( - "messaging/invalid-recipient", - "messaging/invalid-registration-token", - "messaging/registration-token-not-registered" - ) - - override fun notify(msisdn: String, title: String, body: String) { - println("Will try to notify msisdn : $msisdn") - sendNotification(msisdn, title, body) - } - - private fun sendNotification(msisdn: String, title: String, body: String) { - - val store = getResource() - - // This registration token comes from the client FCM SDKs. - val applicationTokens = store.getNotificationTokens(msisdn) - - for (applicationToken in applicationTokens) { - - if (applicationToken.tokenType.equals("FCM")) { - // See documentation on defining a message payload. - val message = Message.builder() - .setNotification(Notification(title, body)) - .setToken(applicationToken.token) - .build() - - // Send a message to the device corresponding to the provided - // registration token. - val future = FirebaseMessaging - .getInstance(FirebaseApp.getInstance("fcm")) - .sendAsync(message) - - val apiFutureCallback = object : ApiFutureCallback { - override fun onSuccess(result: String) { - println("Notification completed with result: $result") - if (listOfFailureCodes.contains(result)) { - store.removeNotificationToken(msisdn, applicationToken.applicationID) - } - } - - override fun onFailure(t: Throwable) { - println("Notification failed with error: $t") - } - } - addCallback(future, apiFutureCallback) - } - } - } -} \ No newline at end of file diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt index da4a2d12c..1ff79d38f 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt @@ -12,53 +12,25 @@ import java.io.IOException import java.nio.file.Files import java.nio.file.Paths -@JsonTypeName("firebase-app-notifier") -class FirebaseModule : PrimeModule { +@JsonTypeName("stripe-payment-processor") +class PaymentProcessorModule : PrimeModule { @JsonProperty("config") - fun setConfig(config: FirebaseConfig) { - println("Config set for AppNotifier") - setupFirebaseApp(config.databaseName, config.configFile) + fun setConfig(config: PaymentProcessorConfig) { + setUpPaymentProcessor() } - private fun setupFirebaseApp( - databaseName: String, - configFile: String) { - - try { - println("Setting up Firebase for FirebaseAppNotifier. databaseName : $databaseName , configFile : $configFile ") - val credentials: GoogleCredentials = if (Files.exists(Paths.get(configFile))) { - FileInputStream(configFile).use { serviceAccount -> GoogleCredentials.fromStream(serviceAccount) } - } else { - GoogleCredentials.getApplicationDefault() - } - - val options = FirebaseOptions.Builder() - .setCredentials(credentials) - .setDatabaseUrl("https://$databaseName.firebaseio.com/") - .build() - try { - FirebaseApp.getInstance("fcm") - } catch (e: Exception) { - FirebaseApp.initializeApp(options, "fcm") - } - - // (un)comment next line to turn on/of extended debugging - // from firebase. - // this.firebaseDatabase.setLogLevel(com.google.firebase.database.Logger.Level.DEBUG); - } catch (ex: IOException) { - throw AppNotifierException(ex) - } + private fun setUpPaymentProcessor() { } } -class FirebaseConfig { +class PaymentProcessorConfig { @NotEmpty - @JsonProperty("databaseName") + @JsonProperty("something") // XXX Replace with Stripe config parameters lateinit var databaseName: String @NotEmpty - @JsonProperty("configFile") + @JsonProperty("somethingelse") // XXX Replace with Stripe config parameters lateinit var configFile: String } \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier deleted file mode 100644 index a8cc68ad4..000000000 --- a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier +++ /dev/null @@ -1 +0,0 @@ -org.ostelco.prime.appnotifier.FirebaseAppNotifier \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule deleted file mode 100644 index 40b4ef01e..000000000 --- a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule +++ /dev/null @@ -1 +0,0 @@ -org.ostelco.prime.appnotifier.FirebaseModule \ No newline at end of file From 1518558552558d5fd825b65dc5d7c4a3c7267ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 19:29:19 +0200 Subject: [PATCH 007/118] Make the intiating actor look like a human --- payment-processor/doc/diagrams/purchase-flow.svg | 4 ++-- payment-processor/doc/puml/purchase-flow.puml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg index 7e04bf9e2..abe9149e7 100644 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -1,6 +1,6 @@ -HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy Prime: Buy(prod, paymentToken, etc.) From 7d584575a0a10d61b232f8b2d64c6562442e54c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Cederl=C3=B6f?= Date: Thu, 28 Jun 2018 12:36:54 +0200 Subject: [PATCH 008/118] Update purchase-flow.puml --- payment-processor/doc/puml/purchase-flow.puml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index 26876965d..5ed049f55 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -1,10 +1,11 @@ @startuml -actor Handset -Handset -> Stripe : CC-info -Stripe -> Handset: PaymentToken -Handset --> Prime: Buy(prod, paymentToken, etc.) -Prime -> Stripe: Buy(token, ...) -Stripe --> Prime: Ok -Prime ---> Handset: Response of buy thingy + +Client -> Client : Collecting payment information +Client -> Stripe : Checkout +Stripe -> Client: {token} +Client -> Prime: Post /products {SKU, token} +Prime -> Stripe: Charge {token} +Stripe -> Prime: Charge Object {id,result..} +Prime -> Client: {result} @enduml From c329663ceb31824210f8c2efb6541eb98be3069c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 14:00:20 +0200 Subject: [PATCH 009/118] New diagram --- .../doc/diagrams/purchase-flow.svg | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg index abe9149e7..b0142f332 100644 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -1,12 +1,13 @@ -HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy \ No newline at end of file From 6f9b3a9dcd620eadc397d6e9f98631dace65a6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 14:02:44 +0200 Subject: [PATCH 011/118] Making method returns dotted --- payment-processor/doc/diagrams/purchase-flow.svg | 8 ++++---- payment-processor/doc/puml/purchase-flow.puml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg index b0142f332..2c588662b 100644 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -1,13 +1,13 @@ -ClientClientStripeStripePrimePrimeCollecting payment informationCheckout{token}Post /products {SKU, token}Charge {token}Charge Object {id,result..}{result} Client: {token} Client -> Prime: Post /products {SKU, token} Prime -> Stripe: Charge {token} -Stripe -> Prime: Charge Object {id,result..} -Prime -> Client: {result} +Stripe --> Prime: Charge Object {id,result..} +Prime --> Client: {result} @enduml From 60041693b17a2116e705b798358b5d7d9d418efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 14:05:09 +0200 Subject: [PATCH 012/118] Make client into a stick figure --- payment-processor/doc/diagrams/purchase-flow.svg | 4 ++-- payment-processor/doc/puml/purchase-flow.puml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg index 2c588662b..b0b6fdee5 100644 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -1,6 +1,6 @@ -ClientClientStripeStripePrimePrimeCollecting payment informationCheckout{token}Post /products {SKU, token}Charge {token}Charge Object {id,result..}{result} Client: {token} From 92b94e775e382f9b9e6d6745665528f88c9fb082 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 28 Jun 2018 14:45:45 +0200 Subject: [PATCH 013/118] Updated sequence diagram for payment --- payment-processor/doc/puml/purchase-flow.puml | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index c2ad1d057..49cca323d 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -1,11 +1,31 @@ @startuml -actor Client + +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant "storage" +end box +participant Stripe + +activate Client Client -> Client : Collecting payment information Client -> Stripe : Checkout +activate Stripe Stripe --> Client: {token} -Client -> Prime: Post /products {SKU, token} -Prime -> Stripe: Charge {token} -Stripe --> Prime: Charge Object {id,result..} -Prime --> Client: {result} - +deactivate Stripe +Client -> "client-api": POST /products {SKU, token} +activate "client-api" +"client-api" -> "payment-processor" +activate "payment-processor" +"payment-processor" -> Stripe: Charge {token} +activate Stripe +Stripe --> "payment-processor": Charge Object {id,result..} +deactivate Stripe +"payment-processor" -> "client-api" +deactivate "payment-processor" +"client-api" --> Client: {result} +deactivate "client-api" +deactivate Client @enduml From 8b6233ebf31167d7af9207bce874546fde4b9389 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 28 Jun 2018 15:43:14 +0200 Subject: [PATCH 014/118] Saving Purchase states in Storage. Experimenting with indenting puml file. --- payment-processor/doc/puml/purchase-flow.puml | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index 49cca323d..4e66c03c2 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -4,28 +4,64 @@ participant Client box "Prime" participant "client-api" + participant OCS + participant Storage participant "payment-processor" - participant "storage" end box participant Stripe activate Client -Client -> Client : Collecting payment information -Client -> Stripe : Checkout -activate Stripe -Stripe --> Client: {token} -deactivate Stripe -Client -> "client-api": POST /products {SKU, token} -activate "client-api" -"client-api" -> "payment-processor" -activate "payment-processor" -"payment-processor" -> Stripe: Charge {token} -activate Stripe -Stripe --> "payment-processor": Charge Object {id,result..} -deactivate Stripe -"payment-processor" -> "client-api" -deactivate "payment-processor" -"client-api" --> Client: {result} -deactivate "client-api" + Client -> Client : Collecting payment information + + Client -> Stripe : Checkout + activate Stripe + Client <-- Stripe: {token} + deactivate Stripe + + Client -> "client-api": POST /products {SKU, token} + activate "client-api" + + "client-api" -> Storage : State { Purchase Initiated } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> "payment-processor" : Perform payment + activate "payment-processor" + + "payment-processor" -> Stripe: Charge {token} + activate Stripe + "payment-processor" <-- Stripe : Charge Object {id,result..} + deactivate Stripe + + "client-api" <-- "payment-processor" + deactivate "payment-processor" + + "client-api" -> Storage : State { Payment Completed } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> OCS : Notify OCS + activate OCS + + OCS -> Storage : Update Balance + activate Storage + OCS <-- Storage + deactivate Storage + + "client-api" <-- OCS + deactivate OCS + + "client-api" -> Storage : State { Balance Updated } \nSave Purchase Record + activate Storage + "client-api" <-- Storage + deactivate Storage + + + Client <-- "client-api": {result} + deactivate "client-api" + deactivate Client + @enduml From d6e8a15bebd96033bfb4535e2f1b483b109d7558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 21:14:09 +0200 Subject: [PATCH 015/118] update figure --- .../doc/diagrams/purchase-flow.svg | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg index b0b6fdee5..78dcb3b6c 100644 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ b/payment-processor/doc/diagrams/purchase-flow.svg @@ -1,13 +1,68 @@ -ClientClientStripeStripePrimePrimeCollecting payment informationCheckout{token}Post /products {SKU, token}Charge {token}Charge Object {id,result..}{result} \ No newline at end of file diff --git a/payment-processor/doc/generate-diagrams.sh b/payment-processor/doc/generate-diagrams.sh index 405ebfa24..4b90fc733 100755 --- a/payment-processor/doc/generate-diagrams.sh +++ b/payment-processor/doc/generate-diagrams.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash plantuml -tsvg -pipe < puml/purchase-flow.puml > diagrams/purchase-flow.svg +plantuml -tsvg -pipe < puml/purchase-state-diagram.puml > diagrams/purchase-state-diagram.svg diff --git a/payment-processor/doc/puml/purchase-state-diagram.puml b/payment-processor/doc/puml/purchase-state-diagram.puml new file mode 100644 index 000000000..82b77b0ca --- /dev/null +++ b/payment-processor/doc/puml/purchase-state-diagram.puml @@ -0,0 +1,14 @@ +@startuml + + +[*] -> PurchaseInitiated +PurchaseInitiated -> PaymentCompleted +PurchaseInitiated -> Rollback +PaymentCompleted -> BalanceUpdated +PaymentCompleted -> Rollback +BalanceUpdated -> Success +BalanceUpdated -> Rollback +Rollback -> Failed +Success -> [*] +Failed -> [*] +@enduml From 765c5e7c713b7ca7879c20e4101bb75feaf48ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:47:07 +0200 Subject: [PATCH 020/118] First (and wildly buggy) stab at payment processor --- payment-processor/doc/puml/signin-flow.puml | 9 +++ .../paymentprocessor/FirebaseAppNotifier.kt | 69 +++++++++++++++++++ .../prime/paymentprocessor/FirebaseModule.kt | 64 +++++++++++++++++ .../org.ostelco.prime.appnotifier.AppNotifier | 1 + .../org.ostelco.prime.module.PrimeModule | 1 + 5 files changed, 144 insertions(+) create mode 100644 payment-processor/doc/puml/signin-flow.puml create mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt create mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt create mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier create mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/signin-flow.puml new file mode 100644 index 000000000..9fe4e0c8a --- /dev/null +++ b/payment-processor/doc/puml/signin-flow.puml @@ -0,0 +1,9 @@ +@startuml + +Client -> Auth0 : Auth +Auth0 -> Client: Tokens (ID (with email), Access) +Client -> CloudEP: GET /profile (https, with access token) +CloudEP -> Prime: GET /profile (http, with access token) +Prime -> Client: User profile (email, address, etc.) + +@enduml diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt new file mode 100644 index 000000000..70e637207 --- /dev/null +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt @@ -0,0 +1,69 @@ +package org.ostelco.prime.appnotifier + +import com.google.api.core.ApiFutureCallback +import com.google.api.core.ApiFutures.addCallback +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import com.google.firebase.messaging.FirebaseMessaging +import com.google.firebase.messaging.Message +import com.google.firebase.messaging.Notification +import org.ostelco.prime.module.getResource +import org.ostelco.prime.storage.legacy.Storage +import java.io.FileInputStream +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths + +class FirebaseAppNotifier: AppNotifier { + + val listOfFailureCodes = listOf( + "messaging/invalid-recipient", + "messaging/invalid-registration-token", + "messaging/registration-token-not-registered" + ) + + override fun notify(msisdn: String, title: String, body: String) { + println("Will try to notify msisdn : $msisdn") + sendNotification(msisdn, title, body) + } + + private fun sendNotification(msisdn: String, title: String, body: String) { + + val store = getResource() + + // This registration token comes from the client FCM SDKs. + val applicationTokens = store.getNotificationTokens(msisdn) + + for (applicationToken in applicationTokens) { + + if (applicationToken.tokenType.equals("FCM")) { + // See documentation on defining a message payload. + val message = Message.builder() + .setNotification(Notification(title, body)) + .setToken(applicationToken.token) + .build() + + // Send a message to the device corresponding to the provided + // registration token. + val future = FirebaseMessaging + .getInstance(FirebaseApp.getInstance("fcm")) + .sendAsync(message) + + val apiFutureCallback = object : ApiFutureCallback { + override fun onSuccess(result: String) { + println("Notification completed with result: $result") + if (listOfFailureCodes.contains(result)) { + store.removeNotificationToken(msisdn, applicationToken.applicationID) + } + } + + override fun onFailure(t: Throwable) { + println("Notification failed with error: $t") + } + } + addCallback(future, apiFutureCallback) + } + } + } +} \ No newline at end of file diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt new file mode 100644 index 000000000..da4a2d12c --- /dev/null +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt @@ -0,0 +1,64 @@ +package org.ostelco.prime.appnotifier + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonTypeName +import com.google.auth.oauth2.GoogleCredentials +import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseOptions +import org.hibernate.validator.constraints.NotEmpty +import org.ostelco.prime.module.PrimeModule +import java.io.FileInputStream +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths + +@JsonTypeName("firebase-app-notifier") +class FirebaseModule : PrimeModule { + + @JsonProperty("config") + fun setConfig(config: FirebaseConfig) { + println("Config set for AppNotifier") + setupFirebaseApp(config.databaseName, config.configFile) + } + + private fun setupFirebaseApp( + databaseName: String, + configFile: String) { + + try { + println("Setting up Firebase for FirebaseAppNotifier. databaseName : $databaseName , configFile : $configFile ") + val credentials: GoogleCredentials = if (Files.exists(Paths.get(configFile))) { + FileInputStream(configFile).use { serviceAccount -> GoogleCredentials.fromStream(serviceAccount) } + } else { + GoogleCredentials.getApplicationDefault() + } + + val options = FirebaseOptions.Builder() + .setCredentials(credentials) + .setDatabaseUrl("https://$databaseName.firebaseio.com/") + .build() + try { + FirebaseApp.getInstance("fcm") + } catch (e: Exception) { + FirebaseApp.initializeApp(options, "fcm") + } + + // (un)comment next line to turn on/of extended debugging + // from firebase. + // this.firebaseDatabase.setLogLevel(com.google.firebase.database.Logger.Level.DEBUG); + } catch (ex: IOException) { + throw AppNotifierException(ex) + } + } +} + +class FirebaseConfig { + + @NotEmpty + @JsonProperty("databaseName") + lateinit var databaseName: String + + @NotEmpty + @JsonProperty("configFile") + lateinit var configFile: String +} \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier new file mode 100644 index 000000000..a8cc68ad4 --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier @@ -0,0 +1 @@ +org.ostelco.prime.appnotifier.FirebaseAppNotifier \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule new file mode 100644 index 000000000..40b4ef01e --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule @@ -0,0 +1 @@ +org.ostelco.prime.appnotifier.FirebaseModule \ No newline at end of file From 7d199b717fe86db40088e18c5ef4db2ecb9d0816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:51:58 +0200 Subject: [PATCH 021/118] Put in the placeholder diagram, not correct, but something that can be corrected. --- payment-processor/doc/puml/signin-flow.puml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/signin-flow.puml index 9fe4e0c8a..2b3a45d91 100644 --- a/payment-processor/doc/puml/signin-flow.puml +++ b/payment-processor/doc/puml/signin-flow.puml @@ -1,9 +1,10 @@ @startuml -Client -> Auth0 : Auth -Auth0 -> Client: Tokens (ID (with email), Access) -Client -> CloudEP: GET /profile (https, with access token) -CloudEP -> Prime: GET /profile (http, with access token) -Prime -> Client: User profile (email, address, etc.) +Handset -> Stripe : CC-info +Stripe -> Handset: PaymentToken +Handset -> Prime: Buy(prod, paymentToken, etc.) +Prime -> Stripe: Buy(token, ...) +Stripe -> Prime: Ok +Prime -> Handset: Response of buy thingy @enduml From cf319888815e1cffd36380030cf7e1fa2ebec951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 15:53:44 +0200 Subject: [PATCH 022/118] Placeholder diagram and readme file --- .../doc/diagrams/signin-flow.svg | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 payment-processor/doc/diagrams/signin-flow.svg diff --git a/payment-processor/doc/diagrams/signin-flow.svg b/payment-processor/doc/diagrams/signin-flow.svg new file mode 100644 index 000000000..7384fa360 --- /dev/null +++ b/payment-processor/doc/diagrams/signin-flow.svg @@ -0,0 +1,23 @@ +HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy \ No newline at end of file From c7340a8c3bb877105334dde01eda5fed239df1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 17:21:03 +0200 Subject: [PATCH 023/118] clarify dummy diagram --- payment-processor/doc/puml/purchase-flow.puml | 70 ++----------------- payment-processor/doc/puml/signin-flow.puml | 10 --- 2 files changed, 6 insertions(+), 74 deletions(-) delete mode 100644 payment-processor/doc/puml/signin-flow.puml diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index 5945f28da..858761ab0 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -1,68 +1,10 @@ @startuml -actor Client -participant Client - -box "Prime" - participant "client-api" - participant OCS - participant Storage - participant "payment-processor" -end box -participant Stripe - -activate Client - Client -> Client : Collecting payment information - - Client -> Stripe : Checkout - activate Stripe - Client <-- Stripe: {token} - deactivate Stripe - - Client -> "client-api": POST /products {SKU, token} - activate "client-api" - - "client-api" -> Storage : State { Purchase Initiated } - activate Storage - "client-api" <-- Storage - deactivate Storage - - "client-api" -> "payment-processor" : Perform payment - activate "payment-processor" - - "payment-processor" -> Stripe: Charge {token} - activate Stripe - "payment-processor" <-- Stripe : Charge Object {id,result..} - deactivate Stripe - - "client-api" <-- "payment-processor" - deactivate "payment-processor" - - "client-api" -> Storage : State { Payment Completed } - activate Storage - "client-api" <-- Storage - deactivate Storage - - "client-api" -> OCS : Notify OCS - activate OCS - - OCS -> Storage : Update Balance - activate Storage - OCS <-- Storage - deactivate Storage - - "client-api" <-- OCS - deactivate OCS - - "client-api" -> Storage : State { Balance Updated } \nSave Purchase Record - activate Storage - "client-api" <-- Storage - deactivate Storage - - - Client <-- "client-api": {result} - deactivate "client-api" - -deactivate Client +Handset -> Stripe : CC-info +Stripe -> Handset: PaymentToken +Handset --> Prime: Buy(prod, paymentToken, etc.) +Prime -> Stripe: Buy(token, ...) +Stripe --> Prime: Ok +Prime ---> Handset: Response of buy thingy @enduml diff --git a/payment-processor/doc/puml/signin-flow.puml b/payment-processor/doc/puml/signin-flow.puml deleted file mode 100644 index 2b3a45d91..000000000 --- a/payment-processor/doc/puml/signin-flow.puml +++ /dev/null @@ -1,10 +0,0 @@ -@startuml - -Handset -> Stripe : CC-info -Stripe -> Handset: PaymentToken -Handset -> Prime: Buy(prod, paymentToken, etc.) -Prime -> Stripe: Buy(token, ...) -Stripe -> Prime: Ok -Prime -> Handset: Response of buy thingy - -@enduml From ed69f57bf0f6eba6f79e09fb16213e78e501ed5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Wed, 27 Jun 2018 17:21:50 +0200 Subject: [PATCH 024/118] Strip away scaffolding to make this thing clearer. --- .../paymentprocessor/FirebaseAppNotifier.kt | 69 ------------------- .../prime/paymentprocessor/FirebaseModule.kt | 44 +++--------- .../org.ostelco.prime.appnotifier.AppNotifier | 1 - .../org.ostelco.prime.module.PrimeModule | 1 - 4 files changed, 8 insertions(+), 107 deletions(-) delete mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt delete mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier delete mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt deleted file mode 100644 index 70e637207..000000000 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseAppNotifier.kt +++ /dev/null @@ -1,69 +0,0 @@ -package org.ostelco.prime.appnotifier - -import com.google.api.core.ApiFutureCallback -import com.google.api.core.ApiFutures.addCallback -import com.google.auth.oauth2.GoogleCredentials -import com.google.firebase.FirebaseApp -import com.google.firebase.FirebaseOptions -import com.google.firebase.messaging.FirebaseMessaging -import com.google.firebase.messaging.Message -import com.google.firebase.messaging.Notification -import org.ostelco.prime.module.getResource -import org.ostelco.prime.storage.legacy.Storage -import java.io.FileInputStream -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths - -class FirebaseAppNotifier: AppNotifier { - - val listOfFailureCodes = listOf( - "messaging/invalid-recipient", - "messaging/invalid-registration-token", - "messaging/registration-token-not-registered" - ) - - override fun notify(msisdn: String, title: String, body: String) { - println("Will try to notify msisdn : $msisdn") - sendNotification(msisdn, title, body) - } - - private fun sendNotification(msisdn: String, title: String, body: String) { - - val store = getResource() - - // This registration token comes from the client FCM SDKs. - val applicationTokens = store.getNotificationTokens(msisdn) - - for (applicationToken in applicationTokens) { - - if (applicationToken.tokenType.equals("FCM")) { - // See documentation on defining a message payload. - val message = Message.builder() - .setNotification(Notification(title, body)) - .setToken(applicationToken.token) - .build() - - // Send a message to the device corresponding to the provided - // registration token. - val future = FirebaseMessaging - .getInstance(FirebaseApp.getInstance("fcm")) - .sendAsync(message) - - val apiFutureCallback = object : ApiFutureCallback { - override fun onSuccess(result: String) { - println("Notification completed with result: $result") - if (listOfFailureCodes.contains(result)) { - store.removeNotificationToken(msisdn, applicationToken.applicationID) - } - } - - override fun onFailure(t: Throwable) { - println("Notification failed with error: $t") - } - } - addCallback(future, apiFutureCallback) - } - } - } -} \ No newline at end of file diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt index da4a2d12c..1ff79d38f 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt @@ -12,53 +12,25 @@ import java.io.IOException import java.nio.file.Files import java.nio.file.Paths -@JsonTypeName("firebase-app-notifier") -class FirebaseModule : PrimeModule { +@JsonTypeName("stripe-payment-processor") +class PaymentProcessorModule : PrimeModule { @JsonProperty("config") - fun setConfig(config: FirebaseConfig) { - println("Config set for AppNotifier") - setupFirebaseApp(config.databaseName, config.configFile) + fun setConfig(config: PaymentProcessorConfig) { + setUpPaymentProcessor() } - private fun setupFirebaseApp( - databaseName: String, - configFile: String) { - - try { - println("Setting up Firebase for FirebaseAppNotifier. databaseName : $databaseName , configFile : $configFile ") - val credentials: GoogleCredentials = if (Files.exists(Paths.get(configFile))) { - FileInputStream(configFile).use { serviceAccount -> GoogleCredentials.fromStream(serviceAccount) } - } else { - GoogleCredentials.getApplicationDefault() - } - - val options = FirebaseOptions.Builder() - .setCredentials(credentials) - .setDatabaseUrl("https://$databaseName.firebaseio.com/") - .build() - try { - FirebaseApp.getInstance("fcm") - } catch (e: Exception) { - FirebaseApp.initializeApp(options, "fcm") - } - - // (un)comment next line to turn on/of extended debugging - // from firebase. - // this.firebaseDatabase.setLogLevel(com.google.firebase.database.Logger.Level.DEBUG); - } catch (ex: IOException) { - throw AppNotifierException(ex) - } + private fun setUpPaymentProcessor() { } } -class FirebaseConfig { +class PaymentProcessorConfig { @NotEmpty - @JsonProperty("databaseName") + @JsonProperty("something") // XXX Replace with Stripe config parameters lateinit var databaseName: String @NotEmpty - @JsonProperty("configFile") + @JsonProperty("somethingelse") // XXX Replace with Stripe config parameters lateinit var configFile: String } \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier deleted file mode 100644 index a8cc68ad4..000000000 --- a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.appnotifier.AppNotifier +++ /dev/null @@ -1 +0,0 @@ -org.ostelco.prime.appnotifier.FirebaseAppNotifier \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule deleted file mode 100644 index 40b4ef01e..000000000 --- a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule +++ /dev/null @@ -1 +0,0 @@ -org.ostelco.prime.appnotifier.FirebaseModule \ No newline at end of file From 860ddaa523567f80c4a7f87497cd42dde5545514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Cederl=C3=B6f?= Date: Thu, 28 Jun 2018 12:36:54 +0200 Subject: [PATCH 025/118] Update purchase-flow.puml --- payment-processor/doc/puml/purchase-flow.puml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index 858761ab0..5ed049f55 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -1,10 +1,11 @@ @startuml -Handset -> Stripe : CC-info -Stripe -> Handset: PaymentToken -Handset --> Prime: Buy(prod, paymentToken, etc.) -Prime -> Stripe: Buy(token, ...) -Stripe --> Prime: Ok -Prime ---> Handset: Response of buy thingy +Client -> Client : Collecting payment information +Client -> Stripe : Checkout +Stripe -> Client: {token} +Client -> Prime: Post /products {SKU, token} +Prime -> Stripe: Charge {token} +Stripe -> Prime: Charge Object {id,result..} +Prime -> Client: {result} @enduml From e288c4ee9c2ce60cdcaa608daa998c7add196d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 14:01:10 +0200 Subject: [PATCH 026/118] Removing obsolete diagram --- .../doc/diagrams/signin-flow.svg | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 payment-processor/doc/diagrams/signin-flow.svg diff --git a/payment-processor/doc/diagrams/signin-flow.svg b/payment-processor/doc/diagrams/signin-flow.svg deleted file mode 100644 index 7384fa360..000000000 --- a/payment-processor/doc/diagrams/signin-flow.svg +++ /dev/null @@ -1,23 +0,0 @@ -HandsetHandsetStripeStripePrimePrimeCC-infoPaymentTokenBuy(prod, paymentToken, etc.)Buy(token, ...)OkResponse of buy thingy \ No newline at end of file From 3584a3cd753410247f60f44f2885dab7981b6655 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 28 Jun 2018 14:45:45 +0200 Subject: [PATCH 027/118] Updated sequence diagram for payment --- payment-processor/doc/puml/purchase-flow.puml | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index 5ed049f55..c54049c11 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -1,11 +1,32 @@ @startuml +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant "storage" +end box +participant Stripe + +activate Client Client -> Client : Collecting payment information Client -> Stripe : Checkout -Stripe -> Client: {token} -Client -> Prime: Post /products {SKU, token} -Prime -> Stripe: Charge {token} -Stripe -> Prime: Charge Object {id,result..} -Prime -> Client: {result} - +activate Stripe +Stripe --> Client: {token} +deactivate Stripe +Client -> "client-api": POST /products {SKU, token} +activate "client-api" +"client-api" -> "payment-processor" +activate "payment-processor" +"payment-processor" -> Stripe: Charge {token} +activate Stripe +Stripe --> "payment-processor": Charge Object {id,result..} +deactivate Stripe +"payment-processor" -> "client-api" +deactivate "payment-processor" +"client-api" --> Client: {result} +deactivate "client-api" +deactivate Client @enduml From 9dbce6fa45302fbde135cff62df48ad6421bae45 Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Thu, 28 Jun 2018 15:43:14 +0200 Subject: [PATCH 028/118] Saving Purchase states in Storage. Experimenting with indenting puml file. --- payment-processor/doc/puml/purchase-flow.puml | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml index c54049c11..5945f28da 100644 --- a/payment-processor/doc/puml/purchase-flow.puml +++ b/payment-processor/doc/puml/purchase-flow.puml @@ -5,28 +5,64 @@ participant Client box "Prime" participant "client-api" + participant OCS + participant Storage participant "payment-processor" - participant "storage" end box participant Stripe activate Client -Client -> Client : Collecting payment information -Client -> Stripe : Checkout -activate Stripe -Stripe --> Client: {token} -deactivate Stripe -Client -> "client-api": POST /products {SKU, token} -activate "client-api" -"client-api" -> "payment-processor" -activate "payment-processor" -"payment-processor" -> Stripe: Charge {token} -activate Stripe -Stripe --> "payment-processor": Charge Object {id,result..} -deactivate Stripe -"payment-processor" -> "client-api" -deactivate "payment-processor" -"client-api" --> Client: {result} -deactivate "client-api" + Client -> Client : Collecting payment information + + Client -> Stripe : Checkout + activate Stripe + Client <-- Stripe: {token} + deactivate Stripe + + Client -> "client-api": POST /products {SKU, token} + activate "client-api" + + "client-api" -> Storage : State { Purchase Initiated } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> "payment-processor" : Perform payment + activate "payment-processor" + + "payment-processor" -> Stripe: Charge {token} + activate Stripe + "payment-processor" <-- Stripe : Charge Object {id,result..} + deactivate Stripe + + "client-api" <-- "payment-processor" + deactivate "payment-processor" + + "client-api" -> Storage : State { Payment Completed } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> OCS : Notify OCS + activate OCS + + OCS -> Storage : Update Balance + activate Storage + OCS <-- Storage + deactivate Storage + + "client-api" <-- OCS + deactivate OCS + + "client-api" -> Storage : State { Balance Updated } \nSave Purchase Record + activate Storage + "client-api" <-- Storage + deactivate Storage + + + Client <-- "client-api": {result} + deactivate "client-api" + deactivate Client + @enduml From 8447606ffd6a3ba1b4cbb174a463ed31fb360936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 21:16:25 +0200 Subject: [PATCH 029/118] Remove fluff --- .../org/ostelco/prime/paymentprocessor/FirebaseModule.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt index 1ff79d38f..fffb48e31 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt @@ -17,10 +17,6 @@ class PaymentProcessorModule : PrimeModule { @JsonProperty("config") fun setConfig(config: PaymentProcessorConfig) { - setUpPaymentProcessor() - } - - private fun setUpPaymentProcessor() { } } From 5311aaadf7758ff3801c039becbd59053c22e9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Remseth?= Date: Thu, 28 Jun 2018 21:55:54 +0200 Subject: [PATCH 030/118] Module skeleton --- .../prime/paymentprocessor/FirebaseModule.kt | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt deleted file mode 100644 index fffb48e31..000000000 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/FirebaseModule.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.ostelco.prime.appnotifier - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.annotation.JsonTypeName -import com.google.auth.oauth2.GoogleCredentials -import com.google.firebase.FirebaseApp -import com.google.firebase.FirebaseOptions -import org.hibernate.validator.constraints.NotEmpty -import org.ostelco.prime.module.PrimeModule -import java.io.FileInputStream -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths - -@JsonTypeName("stripe-payment-processor") -class PaymentProcessorModule : PrimeModule { - - @JsonProperty("config") - fun setConfig(config: PaymentProcessorConfig) { - } -} - -class PaymentProcessorConfig { - - @NotEmpty - @JsonProperty("something") // XXX Replace with Stripe config parameters - lateinit var databaseName: String - - @NotEmpty - @JsonProperty("somethingelse") // XXX Replace with Stripe config parameters - lateinit var configFile: String -} \ No newline at end of file From 7b16f6b74315793a5e075923746a208e91de1d33 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Wed, 11 Jul 2018 11:52:39 +0200 Subject: [PATCH 031/118] Add new Sequence diagrams --- payment-processor/doc/puml/list-cards.puml | 38 ++++++++++ .../doc/puml/purchase-with-newcard.puml | 70 +++++++++++++++++++ .../doc/puml/purchase-with-savedcard.puml | 61 ++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 payment-processor/doc/puml/list-cards.puml create mode 100644 payment-processor/doc/puml/purchase-with-newcard.puml create mode 100644 payment-processor/doc/puml/purchase-with-savedcard.puml diff --git a/payment-processor/doc/puml/list-cards.puml b/payment-processor/doc/puml/list-cards.puml new file mode 100644 index 000000000..f8a01525d --- /dev/null +++ b/payment-processor/doc/puml/list-cards.puml @@ -0,0 +1,38 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant "Storage" +end box +participant Stripe + +activate Client + Client -> "client-api": GET /cards + activate "client-api" + + "client-api" -> "payment-processor" : getSavedCards + activate "payment-processor" + + "payment-processor" -> Storage : getStripeCustomerId + activate Storage + "payment-processor" <-- Storage + deactivate Storage + + "payment-processor" -> Stripe: GET /customer + activate Stripe + "payment-processor" <-- Stripe : Customer Object {sources..} + deactivate Stripe + + "client-api" <-- "payment-processor" : { Source List } + deactivate "payment-processor" + + Client <-- "client-api": {result} + deactivate "client-api" + +deactivate Client + +@enduml \ No newline at end of file diff --git a/payment-processor/doc/puml/purchase-with-newcard.puml b/payment-processor/doc/puml/purchase-with-newcard.puml new file mode 100644 index 000000000..088e37cd2 --- /dev/null +++ b/payment-processor/doc/puml/purchase-with-newcard.puml @@ -0,0 +1,70 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant Storage +end box +participant Stripe + +activate Client + Client -> Client : Collecting payment information + + Client -> Stripe : Create Source + activate Stripe + Client <-- Stripe: {source_id} + deactivate Stripe + + Client -> "client-api": POST /products {SKU, source_id, save-card-flag} + activate "client-api" + + "client-api" -> Storage : State { Purchase Initiated } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> "payment-processor" : Perform payment + activate "payment-processor" + + "payment-processor" -> Storage : Get Stripe Customer Id + activate Storage + "payment-processor" <-- Storage : {customer_id} + deactivate Storage + + "payment-processor" -> Storage : Get Payment Info for SKU + activate Storage + "payment-processor" <-- Storage : {payment_info} + deactivate Storage + + "payment-processor" -> Stripe: Attach source to Customer {source_id, customer_id} + activate Stripe + "payment-processor" <-- Stripe : Source Object {id,metadata..} + deactivate Stripe + + "payment-processor" -> Stripe: Create Charge {source_id, customer_id, payment_info} + activate Stripe + "payment-processor" <-- Stripe : Charge Object {id,result..} + deactivate Stripe + + alt save-card-flag is false + "payment-processor" -> Stripe: Detach Source from customer {source_id, customer_id} + end + + "payment-processor" -> Storage : Save Payment Record + "payment-processor" -> Storage : update balance + + "payment-processor" -> OCS : Notify OCS + + "client-api" <-- "payment-processor" + deactivate "payment-processor" + + Client <-- "client-api": {result} + deactivate "client-api" + +deactivate Client + +@enduml \ No newline at end of file diff --git a/payment-processor/doc/puml/purchase-with-savedcard.puml b/payment-processor/doc/puml/purchase-with-savedcard.puml new file mode 100644 index 000000000..215cb98f1 --- /dev/null +++ b/payment-processor/doc/puml/purchase-with-savedcard.puml @@ -0,0 +1,61 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant Storage +end box +participant Stripe + +activate Client + Client -> "client-api" : Get saved sources + activate "client-api" + Client <-- "client-api": {list of sources} + deactivate "client-api" + + Client -> Client : Choose source for payment + + Client -> "client-api": POST /products {SKU, source_id} + activate "client-api" + + "client-api" -> Storage : State { Purchase Initiated } + activate Storage + "client-api" <-- Storage + deactivate Storage + + "client-api" -> "payment-processor" : Perform payment + activate "payment-processor" + + "payment-processor" -> Storage : Get Stripe Customer Id + activate Storage + "payment-processor" <-- Storage : {customer_id} + deactivate Storage + + "payment-processor" -> Storage : Get Payment Info for SKU + activate Storage + "payment-processor" <-- Storage : {payment_info} + deactivate Storage + + "payment-processor" -> Stripe: Create Charge {source_id, customer_id, payment_info} + activate Stripe + "payment-processor" <-- Stripe : Charge Object {id,result..} + deactivate Stripe + + "payment-processor" -> Storage : Save Payment Record + "payment-processor" -> Storage : update balance + + "payment-processor" -> OCS : Notify OCS + + "client-api" <-- "payment-processor" + deactivate "payment-processor" + + Client <-- "client-api": {result} + deactivate "client-api" + +deactivate Client + +@enduml \ No newline at end of file From 7e93918d95f47b65cd7c2276a64798d942a9ea21 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 11 Jul 2018 14:15:10 +0200 Subject: [PATCH 032/118] Adds sequence diagrams for adding products and plans --- payment-processor/doc/puml/create-plan.puml | 40 +++++++++++++++++++ .../doc/puml/create-product.puml | 35 ++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 payment-processor/doc/puml/create-plan.puml create mode 100644 payment-processor/doc/puml/create-product.puml diff --git a/payment-processor/doc/puml/create-plan.puml b/payment-processor/doc/puml/create-plan.puml new file mode 100644 index 000000000..197192dae --- /dev/null +++ b/payment-processor/doc/puml/create-plan.puml @@ -0,0 +1,40 @@ +@startuml + +actor Admin +participant Admin + +box "Prime" + participant "admin-api" + participant "payment-processor" + participant Storage +end box +participant Stripe + +activate Admin + "Admin" -> "admin-api" : POST /plans {sku} + activate "admin-api" + "admin-api" -> "payment-processor" : POST /plans {sku} + + activate "payment-processor" + "payment-processor" -> "Storage" : Read product {sku} + activate "Storage" + "Storage" -> "payment-processor" : {prod-id} (e.g. Stripe prod-id) + deactivate "Storage" + + "payment-processor" -> "Stripe" : POST /v1/plans {prod-id} (create plan with Stripe) + activate "Stripe" + "Stripe" -> "payment-processor" : {plan-id} + deactivate "Stripe" + + "payment-processor" -> "Storage" : Save plan as a subscription {plan-id} + activate "Storage" + "Storage" -> "payment-processor" : {subscription-id} + deactivate "Storage" + + "payment-processor" -> "admin-api" : {subscription-id} + deactivate "payment-processor" + "admin-api" -> "Admin" : {subscription-id} + deactivate "admin-api" +deactivate Admin + +@enduml diff --git a/payment-processor/doc/puml/create-product.puml b/payment-processor/doc/puml/create-product.puml new file mode 100644 index 000000000..a6aecda40 --- /dev/null +++ b/payment-processor/doc/puml/create-product.puml @@ -0,0 +1,35 @@ +@startuml + +actor Admin +participant Admin + +box "Prime" + participant "admin-api" + participant "payment-processor" + participant Storage +end box +participant Stripe + +activate Admin + "Admin" -> "admin-api" : POST /products (create product) + activate "admin-api" + "admin-api" -> "payment-processor" : POST /products (create product) + + activate "payment-processor" + "payment-processor" -> "Stripe" : POST /v1/products (create product) + activate "Stripe" + "Stripe" -> "payment-processor" : {prod-id} + deactivate "Stripe" + + "payment-processor" -> "Storage" : Save product {prod-id} + activate "Storage" + "Storage" -> "payment-processor" : {sku} + deactivate "Storage" + + "payment-processor" -> "admin-api" : {sku} + deactivate "payment-processor" + "admin-api" -> "Admin" : {sku} + deactivate "admin-api" +deactivate Admin + +@enduml From 0d6f1b3a2f58eb150d504e332e0a816e94c7a58a Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Wed, 11 Jul 2018 15:09:34 +0200 Subject: [PATCH 033/118] Adds sequence diagrams for adding new subscriber --- .../doc/puml/create-new-subscriber.puml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 payment-processor/doc/puml/create-new-subscriber.puml diff --git a/payment-processor/doc/puml/create-new-subscriber.puml b/payment-processor/doc/puml/create-new-subscriber.puml new file mode 100644 index 000000000..0b1ce0556 --- /dev/null +++ b/payment-processor/doc/puml/create-new-subscriber.puml @@ -0,0 +1,59 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant Storage +end box +participant Stripe + +activate Client + Client -> Client : Collecting payment information + + Client -> Stripe : Create Source + activate Stripe + Stripe -> Client : {source-id} + deactivate Stripe + + Client -> "client-api": POST /profile {source-id, email} + activate "client-api" + + "client-api" -> "payment-processor" : Create new profile {source-id, email} + activate "payment-processor" + + "payment-processor" -> Stripe : POST /v1/customers {email} (Create new subscriber with Stripe) + activate Stripe + "Stripe" -> "payment-processor" : {customer-id} + + "payment-processor" -> Stripe : POST /v1/customers/{customer-id}/sources {source-id} + "Stripe" -> "payment-processor" + deactivate Stripe + + "payment-processor" -> Storage : Store profile {customer-id, source-id} + activate Storage + Storage -> "payment-processor" : {profile-id} + + "payment-processor" -> Storage : Get default subscription plan + Storage -> "payment-processor" : {plan-id} + deactivate Storage + + "payment-processor" -> Stripe : POST v1/subscriptions {plan-id, customer-id} + activate Stripe + "Stripe" -> "payment-processor" + deactivate Stripe + + "payment-processor" -> OCS : Notify OCS + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml From c3c0974749c9fc25d7e07b307aa583bb089b2f8b Mon Sep 17 00:00:00 2001 From: Martin Cederlof Date: Wed, 11 Jul 2018 15:15:31 +0200 Subject: [PATCH 034/118] Removed old purchase flow --- payment-processor/doc/puml/purchase-flow.puml | 68 ------------------- .../doc/puml/purchase-with-newcard.puml | 4 -- 2 files changed, 72 deletions(-) delete mode 100644 payment-processor/doc/puml/purchase-flow.puml diff --git a/payment-processor/doc/puml/purchase-flow.puml b/payment-processor/doc/puml/purchase-flow.puml deleted file mode 100644 index 5945f28da..000000000 --- a/payment-processor/doc/puml/purchase-flow.puml +++ /dev/null @@ -1,68 +0,0 @@ -@startuml - -actor Client -participant Client - -box "Prime" - participant "client-api" - participant OCS - participant Storage - participant "payment-processor" -end box -participant Stripe - -activate Client - Client -> Client : Collecting payment information - - Client -> Stripe : Checkout - activate Stripe - Client <-- Stripe: {token} - deactivate Stripe - - Client -> "client-api": POST /products {SKU, token} - activate "client-api" - - "client-api" -> Storage : State { Purchase Initiated } - activate Storage - "client-api" <-- Storage - deactivate Storage - - "client-api" -> "payment-processor" : Perform payment - activate "payment-processor" - - "payment-processor" -> Stripe: Charge {token} - activate Stripe - "payment-processor" <-- Stripe : Charge Object {id,result..} - deactivate Stripe - - "client-api" <-- "payment-processor" - deactivate "payment-processor" - - "client-api" -> Storage : State { Payment Completed } - activate Storage - "client-api" <-- Storage - deactivate Storage - - "client-api" -> OCS : Notify OCS - activate OCS - - OCS -> Storage : Update Balance - activate Storage - OCS <-- Storage - deactivate Storage - - "client-api" <-- OCS - deactivate OCS - - "client-api" -> Storage : State { Balance Updated } \nSave Purchase Record - activate Storage - "client-api" <-- Storage - deactivate Storage - - - Client <-- "client-api": {result} - deactivate "client-api" - -deactivate Client - -@enduml diff --git a/payment-processor/doc/puml/purchase-with-newcard.puml b/payment-processor/doc/puml/purchase-with-newcard.puml index 088e37cd2..3813893d5 100644 --- a/payment-processor/doc/puml/purchase-with-newcard.puml +++ b/payment-processor/doc/puml/purchase-with-newcard.puml @@ -22,10 +22,6 @@ activate Client Client -> "client-api": POST /products {SKU, source_id, save-card-flag} activate "client-api" - "client-api" -> Storage : State { Purchase Initiated } - activate Storage - "client-api" <-- Storage - deactivate Storage "client-api" -> "payment-processor" : Perform payment activate "payment-processor" From 46d8dd9933bf178f16222e5f4a33f9ad7335664d Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 09:05:41 +0200 Subject: [PATCH 035/118] Updates the 'generate-diagrams' script to become slightly more generic --- payment-processor/doc/generate-diagrams.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/payment-processor/doc/generate-diagrams.sh b/payment-processor/doc/generate-diagrams.sh index 4b90fc733..89fa122e8 100755 --- a/payment-processor/doc/generate-diagrams.sh +++ b/payment-processor/doc/generate-diagrams.sh @@ -1,4 +1,17 @@ -#!/usr/bin/env bash +#! /usr/bin/env bash -plantuml -tsvg -pipe < puml/purchase-flow.puml > diagrams/purchase-flow.svg -plantuml -tsvg -pipe < puml/purchase-state-diagram.puml > diagrams/purchase-state-diagram.svg +# Convert 'puml' files. +# Usage: +# generate-diagrams.sh [] +# where '' can be 'svg', 'png' or 'eps'. +# Default is 'svg'. + +FRMT=${1:-svg} +test -d diagrams || mkdir diagrams + +for i in puml/*.puml +do + b=$(basename $i .puml) + echo "converting $i -> diagrams/$b.$FRMT" + plantuml -t$FRMT -pipe < $i > diagrams/$b.$FRMT +done From f19ff69988492fef07057eeb2c01c364f26682e9 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 11:56:44 +0200 Subject: [PATCH 036/118] Update 'purchase with saved card' sequence diagram with more details --- .../purchase-with-savedcard-modified.puml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 payment-processor/doc/puml/purchase-with-savedcard-modified.puml diff --git a/payment-processor/doc/puml/purchase-with-savedcard-modified.puml b/payment-processor/doc/puml/purchase-with-savedcard-modified.puml new file mode 100644 index 000000000..f0ae046a6 --- /dev/null +++ b/payment-processor/doc/puml/purchase-with-savedcard-modified.puml @@ -0,0 +1,68 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant DAO +end box +participant Stripe + +activate Client + Client -> "client-api" : Get saved sources + activate "client-api" + "client-api" -> Client : {list of sources} + deactivate "client-api" + note right : See "list-card" flow diagram + + Client -> Client : Choose source for payment + + Client -> "client-api": POST /products {sku, sourceId} + activate "client-api" + + "client-api" -> "payment-processor" : purchaseProduct(name, sku, sourceId) + activate "payment-processor" + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + "payment-processor" -> DAO : getProduct(sku) + activate DAO + DAO -> "payment-processor" : {product} + deactivate DAO + + alt successful case + "payment-processor" -> Stripe : POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description} + activate Stripe + Stripe -> "payment-processor" : {chargeInfo} + deactivate Stripe + + "payment-processor" -> DAO: recordChargeInfo(name, chargeInfo) + activate DAO + DAO -> "payment-processor" : {result} + deactivate DAO + + "payment-processor" -> OCS : updateBucket(name, product.size) + activate OCS + OCS -> "payment-processor" : {result} + deactivate OCS + + else error + note right of "payment-processor" : Unroll charge with Stripe etc. (TBD) + + end + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml From 44083b3ff7755acfe5e8b4c56191b9db0424708d Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 11:56:44 +0200 Subject: [PATCH 037/118] Update 'purchase with saved card' sequence diagram with more details --- .../doc/puml/purchase-with-savedcard-modified.puml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payment-processor/doc/puml/purchase-with-savedcard-modified.puml b/payment-processor/doc/puml/purchase-with-savedcard-modified.puml index f0ae046a6..ee91e6021 100644 --- a/payment-processor/doc/puml/purchase-with-savedcard-modified.puml +++ b/payment-processor/doc/puml/purchase-with-savedcard-modified.puml @@ -16,7 +16,7 @@ activate Client activate "client-api" "client-api" -> Client : {list of sources} deactivate "client-api" - note right : See "list-card" flow diagram + note right : See "list-sources" flow diagram Client -> Client : Choose source for payment From 2e178ff552ce3d94abc0bca7865109a9615c32c8 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 13:48:25 +0200 Subject: [PATCH 038/118] Adds sequence diagrams for listing sources Replaces the 'list-cards' sequence diagram --- payment-processor/doc/puml/list-sources.puml | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 payment-processor/doc/puml/list-sources.puml diff --git a/payment-processor/doc/puml/list-sources.puml b/payment-processor/doc/puml/list-sources.puml new file mode 100644 index 000000000..8637b599d --- /dev/null +++ b/payment-processor/doc/puml/list-sources.puml @@ -0,0 +1,39 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant "DAO" +end box +participant Stripe + +activate Client + Client -> "client-api": GET /sources + activate "client-api" + + "client-api" -> "payment-processor" : getSavedSources(name) + activate "payment-processor" + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + "payment-processor" -> Stripe : GET /v1/customers/ + activate Stripe + Stripe -> "payment-processor" : {customer} + deactivate Stripe + note right of "payment-processor" : The {customer} object includes a list of sources + + "payment-processor" -> "client-api" : [{source}, ... ] + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml From bbb64e9fa80a629bf2164d40cb38a519e6c1bc07 Mon Sep 17 00:00:00 2001 From: Martin Cederlof Date: Thu, 12 Jul 2018 13:51:43 +0200 Subject: [PATCH 039/118] First cut adding payment processor --- .../ostelco/prime/client/api/TopupModule.kt | 2 + .../client/api/resources/PaymentResource.kt | 39 +++++++++++++++++++ .../prime/client/api/store/SubscriberDAO.kt | 2 + .../client/api/store/SubscriberDAOImpl.kt | 6 +-- .../prime/storage/embeddedgraph/GraphStore.kt | 12 ++++++ .../prime/storage/firebase/FirebaseStorage.kt | 8 ++++ .../ostelco/prime/storage/graph/GraphStore.kt | 12 ++++++ .../kotlin/org/ostelco/prime/ocs/OcsModule.kt | 2 - .../PaymentProcessorModule.kt | 21 +++------- .../StripePaymentProcessor.kt | 7 ++++ ...e => org.ostelco.prime.module.PrimeModule} | 0 ...co.prime.paymentprocessor.PaymentProcessor | 1 + .../paymentprocessor/PaymentProcessor.kt | 5 +++ .../ostelco/prime/storage/legacy/Storage.kt | 15 +++++++ prime/build.gradle | 1 + prime/config/.gitignore | 3 +- prime/config/config.yaml | 3 ++ prime/config/test.yaml | 3 ++ .../integration-tests/resources/config.yaml | 3 ++ settings.gradle | 2 + 20 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt create mode 100644 payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt rename payment-processor/src/main/resources/META-INF/services/{org.ostelco.prime.provider.Service => org.ostelco.prime.module.PrimeModule} (100%) create mode 100644 payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.paymentprocessor.PaymentProcessor create mode 100644 prime-api/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessor.kt diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/TopupModule.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/TopupModule.kt index 90ee03f1e..0f2bf4d27 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/TopupModule.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/TopupModule.kt @@ -19,6 +19,7 @@ import org.ostelco.prime.logger import org.ostelco.prime.module.PrimeModule import org.ostelco.prime.module.getResource import org.ostelco.prime.ocs.OcsSubscriberService +import org.ostelco.prime.paymentprocessor.PaymentProcessor import org.ostelco.prime.storage.legacy.Storage import javax.ws.rs.client.Client @@ -54,6 +55,7 @@ class TopupModule : PrimeModule { jerseyEnv.register(ProfileResource(dao)) jerseyEnv.register(SubscriptionResource(dao, client, config.pseudonymEndpoint!!)) jerseyEnv.register(ApplicationTokenResource(dao)) + jerseyEnv.register(PaymentResource(dao)) /* For reporting OAuth2 caching events. */ val metrics = SharedMetricRegistries.getOrCreate(env.getName()) diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt new file mode 100644 index 000000000..ea1570292 --- /dev/null +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt @@ -0,0 +1,39 @@ +package org.ostelco.prime.client.api.resources + +import io.dropwizard.auth.Auth +import org.ostelco.prime.client.api.auth.AccessTokenPrincipal +import org.ostelco.prime.client.api.store.SubscriberDAO +import org.ostelco.prime.module.getResource +import org.ostelco.prime.paymentprocessor.PaymentProcessor +import javax.ws.rs.Consumes +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.core.Response + +/** + * Payment API. + * + */ +@Path("/payment") +class PaymentResource(private val dao: SubscriberDAO) : ResourceHelpers() { + + private val paymentProcessor by lazy { getResource() } + + @POST + @Consumes("application/json") + fun listSources(@Auth token: AccessTokenPrincipal?): Response { + if (token == null) { + return Response.status(Response.Status.UNAUTHORIZED) + .build() + } + + val paymentId: String = dao.getPaymentId(token.name) + ?: return Response.status(Response.Status.NOT_FOUND).build() + + val sources = paymentProcessor.listSources(paymentId) + + return Response.status(Response.Status.OK) + .entity(asJson(sources)) + .build() + } +} diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt index f3986e237..f0b617a3a 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt @@ -38,6 +38,8 @@ interface SubscriberDAO { fun storeApplicationToken(msisdn: String, applicationToken: ApplicationToken): Either + fun getPaymentId(name: String): String? + companion object { /** diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt index bb7933764..4e5a73848 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt @@ -211,7 +211,7 @@ class SubscriberDAOImpl(private val storage: Storage, private val ocsSubscriberS return Either.right(Consent(consentId, "Grant permission to process personal data", false)) } - override fun reportAnalytics(subscriptionId: String, events: String): Option { - return Option.none() - } + override fun reportAnalytics(subscriptionId: String, events: String): Option = Option.none() + + override fun getPaymentId(name: String): String? = storage.getPaymentId(name) } diff --git a/embedded-graph-store/src/main/kotlin/org/ostelco/prime/storage/embeddedgraph/GraphStore.kt b/embedded-graph-store/src/main/kotlin/org/ostelco/prime/storage/embeddedgraph/GraphStore.kt index d329a4ad0..de45aff6d 100644 --- a/embedded-graph-store/src/main/kotlin/org/ostelco/prime/storage/embeddedgraph/GraphStore.kt +++ b/embedded-graph-store/src/main/kotlin/org/ostelco/prime/storage/embeddedgraph/GraphStore.kt @@ -155,6 +155,18 @@ object GraphStoreSingleton : Storage, AdminDataStore { segmentToSubscriberStore.create(segment.id, segment.subscribers) } + override fun getPaymentId(id: String): String? { + TODO("not implemented") + } + + override fun deletePaymentId(id: String): Boolean { + TODO("not implemented") + } + + override fun createPaymentId(id: String, paymentId: String): Boolean { + TODO("not implemented") + } + // override fun getOffers(): Collection = offerStore.getAll().values.map { Offer().apply { id = it.id } } // override fun getSegments(): Collection = segmentStore.getAll().values.map { Segment().apply { id = it.id } } diff --git a/firebase-store/src/main/kotlin/org/ostelco/prime/storage/firebase/FirebaseStorage.kt b/firebase-store/src/main/kotlin/org/ostelco/prime/storage/firebase/FirebaseStorage.kt index 3abe10306..af7bace2d 100644 --- a/firebase-store/src/main/kotlin/org/ostelco/prime/storage/firebase/FirebaseStorage.kt +++ b/firebase-store/src/main/kotlin/org/ostelco/prime/storage/firebase/FirebaseStorage.kt @@ -40,6 +40,7 @@ object FirebaseStorageSingleton : Storage { private val subscriberEntity = EntityType("subscribers", Subscriber::class.java) private val paymentHistoryEntity = EntityType("paymentHistory", PurchaseRecord::class.java) private val fcmTokenEntity = EntityType("notificationTokens", ApplicationToken::class.java) + private val paymentIdEntity = EntityType("paymentId", String::class.java) private val firebaseDatabase = setupFirebaseInstance(config.databaseName, config.configFile) @@ -49,6 +50,7 @@ object FirebaseStorageSingleton : Storage { private val subscriberStore = EntityStore(firebaseDatabase, subscriberEntity) private val paymentHistoryStore = EntityStore(firebaseDatabase, paymentHistoryEntity) private val fcmTokenStore = EntityStore(firebaseDatabase, fcmTokenEntity) + private val paymentIdStore = EntityStore(firebaseDatabase, paymentIdEntity) private fun setupFirebaseInstance( databaseName: String, @@ -157,6 +159,12 @@ object FirebaseStorageSingleton : Storage { override fun removeNotificationToken(msisdn: String, applicationID: String): Boolean { return fcmTokenStore.delete(applicationID) { databaseReference.child(urlEncode(msisdn)) } } + + override fun getPaymentId(id: String): String? = paymentIdStore.get(id) + + override fun deletePaymentId(id: String): Boolean = paymentIdStore.delete(id) + + override fun createPaymentId(id: String, paymentId: String): Boolean = paymentIdStore.create(id, paymentId) } private val config = FirebaseConfigRegistry.firebaseConfig diff --git a/graph-store/src/main/kotlin/org/ostelco/prime/storage/graph/GraphStore.kt b/graph-store/src/main/kotlin/org/ostelco/prime/storage/graph/GraphStore.kt index 5dc47b9ea..97afe8705 100644 --- a/graph-store/src/main/kotlin/org/ostelco/prime/storage/graph/GraphStore.kt +++ b/graph-store/src/main/kotlin/org/ostelco/prime/storage/graph/GraphStore.kt @@ -183,4 +183,16 @@ object GraphStoreSingleton : Storage, AdminDataStore { // override fun getSegment(id: String): Segment? = segmentStore.get(id)?.let { Segment().apply { this.id = it.id } } // override fun getProductClass(id: String): ProductClass? = productClassStore.get(id) + + override fun getPaymentId(id: String): String? { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun deletePaymentId(id: String): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun createPaymentId(id: String, paymentId: String): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } } \ No newline at end of file diff --git a/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt b/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt index ea3453519..b64393d9b 100644 --- a/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt +++ b/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsModule.kt @@ -5,13 +5,11 @@ import com.fasterxml.jackson.annotation.JsonTypeName import io.dropwizard.setup.Environment import org.hibernate.validator.constraints.NotEmpty import org.ostelco.prime.analytics.DataConsumptionInfoPublisher -import org.ostelco.prime.appnotifier.AppNotifier import org.ostelco.prime.disruptor.ClearingEventHandler import org.ostelco.prime.disruptor.PrimeDisruptor import org.ostelco.prime.disruptor.PrimeEventProducerImpl import org.ostelco.prime.events.EventProcessor import org.ostelco.prime.module.PrimeModule -import org.ostelco.prime.module.getResource import org.ostelco.prime.thresholds.ThresholdChecker @JsonTypeName("ocs") diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessorModule.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessorModule.kt index 68d066df3..30ec75abc 100644 --- a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessorModule.kt +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessorModule.kt @@ -2,36 +2,27 @@ package org.ostelco.prime.paymentprocessor import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonTypeName -import com.google.auth.oauth2.GoogleCredentials -import com.google.firebase.FirebaseApp -import com.google.firebase.FirebaseOptions import io.dropwizard.setup.Environment import org.hibernate.validator.constraints.NotEmpty import org.ostelco.prime.module.PrimeModule -import java.io.FileInputStream -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths @JsonTypeName("stripe-payment-processor") class PaymentProcessorModule : PrimeModule { @JsonProperty("config") fun setConfig(config: PaymentProcessorConfig) { + println("Config set for PaymentProcessor") + println("Secret key is ${config.configFile}") } - @Override - fun init(config: Environment) { + override fun init(env: Environment) { + println("PaymentProcessor init with ${env}") } } class PaymentProcessorConfig { @NotEmpty - @JsonProperty("something") // XXX Replace with Stripe config parameters - lateinit var databaseName: String - - @NotEmpty - @JsonProperty("somethingelse") // XXX Replace with Stripe config parameters + @JsonProperty("configFile") lateinit var configFile: String -} \ No newline at end of file +} diff --git a/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt new file mode 100644 index 000000000..b453d7e49 --- /dev/null +++ b/payment-processor/src/main/kotlin/org/ostelco/prime/paymentprocessor/StripePaymentProcessor.kt @@ -0,0 +1,7 @@ +package org.ostelco.prime.paymentprocessor + +class StripePaymentProcessor : PaymentProcessor { + override fun listSources(stripeUser: String): List { + return emptyList() + } +} \ No newline at end of file diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.provider.Service b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule similarity index 100% rename from payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.provider.Service rename to payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.module.PrimeModule diff --git a/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.paymentprocessor.PaymentProcessor b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.paymentprocessor.PaymentProcessor new file mode 100644 index 000000000..88d4e4d88 --- /dev/null +++ b/payment-processor/src/main/resources/META-INF/services/org.ostelco.prime.paymentprocessor.PaymentProcessor @@ -0,0 +1 @@ +org.ostelco.prime.paymentprocessor.StripePaymentProcessor \ No newline at end of file diff --git a/prime-api/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessor.kt b/prime-api/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessor.kt new file mode 100644 index 000000000..2244d51c6 --- /dev/null +++ b/prime-api/src/main/kotlin/org/ostelco/prime/paymentprocessor/PaymentProcessor.kt @@ -0,0 +1,5 @@ +package org.ostelco.prime.paymentprocessor + +interface PaymentProcessor { + fun listSources(stripeUser: String): List +} \ No newline at end of file diff --git a/prime-api/src/main/kotlin/org/ostelco/prime/storage/legacy/Storage.kt b/prime-api/src/main/kotlin/org/ostelco/prime/storage/legacy/Storage.kt index c9e99c8ff..4b4ac58a1 100644 --- a/prime-api/src/main/kotlin/org/ostelco/prime/storage/legacy/Storage.kt +++ b/prime-api/src/main/kotlin/org/ostelco/prime/storage/legacy/Storage.kt @@ -108,4 +108,19 @@ interface Storage { * Get token used for sending notification to user application */ fun removeNotificationToken(msisdn: String, applicationID: String): Boolean + + /** + * + */ + fun getPaymentId(id: String): String? + + /** + * + */ + fun deletePaymentId(id: String): Boolean + + /** + * + */ + fun createPaymentId(id: String, paymentId: String): Boolean } diff --git a/prime/build.gradle b/prime/build.gradle index 98c3c341e..a078f2f94 100644 --- a/prime/build.gradle +++ b/prime/build.gradle @@ -30,6 +30,7 @@ dependencies { runtimeOnly project(':client-api') runtimeOnly project(':admin-api') runtimeOnly project(':app-notifier') + runtimeOnly project(':payment-processor') implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion" implementation "io.dropwizard:dropwizard-http2:$dropwizardVersion" diff --git a/prime/config/.gitignore b/prime/config/.gitignore index bf045303f..e3d23dec4 100644 --- a/prime/config/.gitignore +++ b/prime/config/.gitignore @@ -1 +1,2 @@ -pantel-prod.json \ No newline at end of file +pantel-prod.json +stripe.json \ No newline at end of file diff --git a/prime/config/config.yaml b/prime/config/config.yaml index 35daaedbe..a7297fa02 100644 --- a/prime/config/config.yaml +++ b/prime/config/config.yaml @@ -16,6 +16,9 @@ modules: pseudonymEndpoint: http://pseudonym-server-service.default.svc.cluster.local jerseyClient: timeout: 2s + - type: stripe-payment-processor + config: + configFile: /secret/stripe.json - type: firebase-app-notifier config: configFile: /secret/pantel-prod.json diff --git a/prime/config/test.yaml b/prime/config/test.yaml index 49692b570..175305aa3 100644 --- a/prime/config/test.yaml +++ b/prime/config/test.yaml @@ -18,6 +18,9 @@ modules: pseudonymEndpoint: http://pseudonym-server:8080 jerseyClient: timeout: 3s + - type: stripe-payment-processor + config: + configFile: /secret/stripe.json - type: firebase-app-notifier config: configFile: /secret/pantel-prod.json diff --git a/prime/src/integration-tests/resources/config.yaml b/prime/src/integration-tests/resources/config.yaml index 274718b7c..0e6959054 100644 --- a/prime/src/integration-tests/resources/config.yaml +++ b/prime/src/integration-tests/resources/config.yaml @@ -15,6 +15,9 @@ modules: pseudonymEndpoint: http://pseudonym-server:8080 jerseyClient: timeout: 3s + - type: stripe-payment-processor + config: + configFile: config/stripe.json - type: firebase-app-notifier config: configFile: config/pantel-prod.json diff --git a/settings.gradle b/settings.gradle index 846979033..9bd371aab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,6 +18,7 @@ include ':ocs' include ':ocs-api' include ':ocsgw' include ':ostelco-lib' +include ':payment-processor' include ':prime' include ':prime-api' include ':prime-client-api' @@ -41,6 +42,7 @@ project(':ocs').projectDir = "$rootDir/ocs" as File project(':ocs-api').projectDir = "$rootDir/ocs-api" as File project(':ocsgw').projectDir = "$rootDir/ocsgw" as File project(':ostelco-lib').projectDir = "$rootDir/ostelco-lib" as File +project(':payment-processor').projectDir = "$rootDir/payment-processor" as File project(':prime').projectDir = "$rootDir/prime" as File project(':prime-api').projectDir = "$rootDir/prime-api" as File project(':prime-client-api').projectDir = "$rootDir/prime-client-api" as File From 45a390dfd72e2439d58925ead2c6e1a2e80463ac Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 13:53:38 +0200 Subject: [PATCH 040/118] Renamed 'purchase-with-savedcard-modified' file --- ...th-savedcard-modified.puml => purchase-with-saved-source.puml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename payment-processor/doc/puml/{purchase-with-savedcard-modified.puml => purchase-with-saved-source.puml} (100%) diff --git a/payment-processor/doc/puml/purchase-with-savedcard-modified.puml b/payment-processor/doc/puml/purchase-with-saved-source.puml similarity index 100% rename from payment-processor/doc/puml/purchase-with-savedcard-modified.puml rename to payment-processor/doc/puml/purchase-with-saved-source.puml From e2b1606b2f9e56f41737ae1422316352afdf241e Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 14:43:52 +0200 Subject: [PATCH 041/118] Updates to 'purchase' flow diagarams --- .../doc/puml/purchase-with-new-source.puml | 88 +++++++++++++++++++ .../doc/puml/purchase-with-saved-source.puml | 8 +- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 payment-processor/doc/puml/purchase-with-new-source.puml diff --git a/payment-processor/doc/puml/purchase-with-new-source.puml b/payment-processor/doc/puml/purchase-with-new-source.puml new file mode 100644 index 000000000..db722acd9 --- /dev/null +++ b/payment-processor/doc/puml/purchase-with-new-source.puml @@ -0,0 +1,88 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant DAO +end box +participant Stripe + +activate Client + Client -> Client : Collecting payment information + + Client -> Stripe : Create new source with Stripe + activate Stripe + Stripe -> Client : {sourceId} + deactivate Stripe + note right : See "create-source" flow diagram + + Client -> Client : Select product ({sku}) + + Client -> "client-api": POST /products {sku, sourceId, saveCardFlag} + activate "client-api" + + "client-api" -> "payment-processor" : purchaseProduct(name, sku, sourceId, saveCardFlag) + activate "payment-processor" + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + "payment-processor" -> DAO : getProduct(sku) + activate DAO + DAO -> "payment-processor" : {product} + deactivate DAO + + alt successful case + + "payment-processor" -> Stripe : POST /v1/customers/{customerId}/sources {sourceId} + activate Stripe + Stripe -> "payment-processor" : {sourceInfo} + deactivate Stripe + note left : Attach new source to customer ({sourceId, customerId}) + + "payment-processor" -> Stripe : POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description} + activate Stripe + Stripe -> "payment-processor" : {chargeInfo} + deactivate Stripe + + alt saveCardFlag is false + "payment-processor" -> Stripe : DELETE /v1/customer/{customerId}/sources/{sourceId} + activate Stripe + Stripe -> "payment-processor" : {result)} + deactivate Stripe + end + + "payment-processor" -> DAO: recordChargeInfo(name, chargeInfo) + activate DAO + DAO -> "payment-processor" : {result} + deactivate DAO + note right + Charges/refunds to be recorded + in a "ledger" type of store + end note + + "payment-processor" -> OCS : updateBucket(name, product.size) + activate OCS + OCS -> "payment-processor" : {result} + deactivate OCS + + else error + note right of "payment-processor" : Unroll charge with Stripe etc. (TBD) + + end + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml diff --git a/payment-processor/doc/puml/purchase-with-saved-source.puml b/payment-processor/doc/puml/purchase-with-saved-source.puml index ee91e6021..0f0c3e7a4 100644 --- a/payment-processor/doc/puml/purchase-with-saved-source.puml +++ b/payment-processor/doc/puml/purchase-with-saved-source.puml @@ -12,13 +12,15 @@ end box participant Stripe activate Client + Client -> "client-api" : Get saved sources activate "client-api" "client-api" -> Client : {list of sources} deactivate "client-api" note right : See "list-sources" flow diagram - Client -> Client : Choose source for payment + Client -> Client : Choose source for payment ({sourceId}) + Client -> Client : Select product ({sku}) Client -> "client-api": POST /products {sku, sourceId} activate "client-api" @@ -46,6 +48,10 @@ activate Client activate DAO DAO -> "payment-processor" : {result} deactivate DAO + note right + Charges/refunds to be recorded + in a "ledger" type of store + end note "payment-processor" -> OCS : updateBucket(name, product.size) activate OCS From 68d4cf1fbd9e3cceb89c6fa5608a6ea5353dd349 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Thu, 12 Jul 2018 14:43:52 +0200 Subject: [PATCH 042/118] Updates to 'purchase' and 'new-source' flow diagarams --- payment-processor/doc/puml/create-source.puml | 44 +++++++++++++++++++ .../doc/puml/purchase-with-new-source.puml | 1 - 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 payment-processor/doc/puml/create-source.puml diff --git a/payment-processor/doc/puml/create-source.puml b/payment-processor/doc/puml/create-source.puml new file mode 100644 index 000000000..a387d823c --- /dev/null +++ b/payment-processor/doc/puml/create-source.puml @@ -0,0 +1,44 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant DAO +end box +participant Stripe + +activate Client + Client -> Client : Collecting payment information + + Client -> Stripe : Create new source with Stripe + activate Stripe + Stripe -> Client : {sourceId} + deactivate Stripe + + Client -> "client-api" : POST /sources {sourceId} + + "client-api" -> "payment-processor" : addSource(name, sourceId) + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + "payment-processor" -> Stripe : POST /v1/customers/{customerId}/sources {sourceId} + activate Stripe + Stripe -> "payment-processor" : {sourceInfo} + deactivate Stripe + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml diff --git a/payment-processor/doc/puml/purchase-with-new-source.puml b/payment-processor/doc/puml/purchase-with-new-source.puml index db722acd9..0ddbf8338 100644 --- a/payment-processor/doc/puml/purchase-with-new-source.puml +++ b/payment-processor/doc/puml/purchase-with-new-source.puml @@ -18,7 +18,6 @@ activate Client activate Stripe Stripe -> Client : {sourceId} deactivate Stripe - note right : See "create-source" flow diagram Client -> Client : Select product ({sku}) From d8a26d279476eb2f3bb1b43461cb5a08c63ccf7d Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 09:29:15 +0200 Subject: [PATCH 043/118] Removes double up puml doc files that has been renamed and updated --- payment-processor/doc/puml/list-cards.puml | 38 ----------- .../doc/puml/purchase-with-newcard.puml | 66 ------------------- .../doc/puml/purchase-with-savedcard.puml | 61 ----------------- 3 files changed, 165 deletions(-) delete mode 100644 payment-processor/doc/puml/list-cards.puml delete mode 100644 payment-processor/doc/puml/purchase-with-newcard.puml delete mode 100644 payment-processor/doc/puml/purchase-with-savedcard.puml diff --git a/payment-processor/doc/puml/list-cards.puml b/payment-processor/doc/puml/list-cards.puml deleted file mode 100644 index f8a01525d..000000000 --- a/payment-processor/doc/puml/list-cards.puml +++ /dev/null @@ -1,38 +0,0 @@ -@startuml - -actor Client -participant Client - -box "Prime" - participant "client-api" - participant "payment-processor" - participant "Storage" -end box -participant Stripe - -activate Client - Client -> "client-api": GET /cards - activate "client-api" - - "client-api" -> "payment-processor" : getSavedCards - activate "payment-processor" - - "payment-processor" -> Storage : getStripeCustomerId - activate Storage - "payment-processor" <-- Storage - deactivate Storage - - "payment-processor" -> Stripe: GET /customer - activate Stripe - "payment-processor" <-- Stripe : Customer Object {sources..} - deactivate Stripe - - "client-api" <-- "payment-processor" : { Source List } - deactivate "payment-processor" - - Client <-- "client-api": {result} - deactivate "client-api" - -deactivate Client - -@enduml \ No newline at end of file diff --git a/payment-processor/doc/puml/purchase-with-newcard.puml b/payment-processor/doc/puml/purchase-with-newcard.puml deleted file mode 100644 index 3813893d5..000000000 --- a/payment-processor/doc/puml/purchase-with-newcard.puml +++ /dev/null @@ -1,66 +0,0 @@ -@startuml - -actor Client -participant Client - -box "Prime" - participant "client-api" - participant "payment-processor" - participant OCS - participant Storage -end box -participant Stripe - -activate Client - Client -> Client : Collecting payment information - - Client -> Stripe : Create Source - activate Stripe - Client <-- Stripe: {source_id} - deactivate Stripe - - Client -> "client-api": POST /products {SKU, source_id, save-card-flag} - activate "client-api" - - - "client-api" -> "payment-processor" : Perform payment - activate "payment-processor" - - "payment-processor" -> Storage : Get Stripe Customer Id - activate Storage - "payment-processor" <-- Storage : {customer_id} - deactivate Storage - - "payment-processor" -> Storage : Get Payment Info for SKU - activate Storage - "payment-processor" <-- Storage : {payment_info} - deactivate Storage - - "payment-processor" -> Stripe: Attach source to Customer {source_id, customer_id} - activate Stripe - "payment-processor" <-- Stripe : Source Object {id,metadata..} - deactivate Stripe - - "payment-processor" -> Stripe: Create Charge {source_id, customer_id, payment_info} - activate Stripe - "payment-processor" <-- Stripe : Charge Object {id,result..} - deactivate Stripe - - alt save-card-flag is false - "payment-processor" -> Stripe: Detach Source from customer {source_id, customer_id} - end - - "payment-processor" -> Storage : Save Payment Record - "payment-processor" -> Storage : update balance - - "payment-processor" -> OCS : Notify OCS - - "client-api" <-- "payment-processor" - deactivate "payment-processor" - - Client <-- "client-api": {result} - deactivate "client-api" - -deactivate Client - -@enduml \ No newline at end of file diff --git a/payment-processor/doc/puml/purchase-with-savedcard.puml b/payment-processor/doc/puml/purchase-with-savedcard.puml deleted file mode 100644 index 215cb98f1..000000000 --- a/payment-processor/doc/puml/purchase-with-savedcard.puml +++ /dev/null @@ -1,61 +0,0 @@ -@startuml - -actor Client -participant Client - -box "Prime" - participant "client-api" - participant "payment-processor" - participant OCS - participant Storage -end box -participant Stripe - -activate Client - Client -> "client-api" : Get saved sources - activate "client-api" - Client <-- "client-api": {list of sources} - deactivate "client-api" - - Client -> Client : Choose source for payment - - Client -> "client-api": POST /products {SKU, source_id} - activate "client-api" - - "client-api" -> Storage : State { Purchase Initiated } - activate Storage - "client-api" <-- Storage - deactivate Storage - - "client-api" -> "payment-processor" : Perform payment - activate "payment-processor" - - "payment-processor" -> Storage : Get Stripe Customer Id - activate Storage - "payment-processor" <-- Storage : {customer_id} - deactivate Storage - - "payment-processor" -> Storage : Get Payment Info for SKU - activate Storage - "payment-processor" <-- Storage : {payment_info} - deactivate Storage - - "payment-processor" -> Stripe: Create Charge {source_id, customer_id, payment_info} - activate Stripe - "payment-processor" <-- Stripe : Charge Object {id,result..} - deactivate Stripe - - "payment-processor" -> Storage : Save Payment Record - "payment-processor" -> Storage : update balance - - "payment-processor" -> OCS : Notify OCS - - "client-api" <-- "payment-processor" - deactivate "payment-processor" - - Client <-- "client-api": {result} - deactivate "client-api" - -deactivate Client - -@enduml \ No newline at end of file From 5a8920aee0355b76835c88ce871afb782e2384fb Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 10:28:10 +0200 Subject: [PATCH 044/118] Updates the 'create-new-subscriber' flow diagram --- .../doc/puml/create-new-subscriber.puml | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/payment-processor/doc/puml/create-new-subscriber.puml b/payment-processor/doc/puml/create-new-subscriber.puml index 0b1ce0556..6f3ba094a 100644 --- a/payment-processor/doc/puml/create-new-subscriber.puml +++ b/payment-processor/doc/puml/create-new-subscriber.puml @@ -16,35 +16,45 @@ activate Client Client -> Stripe : Create Source activate Stripe - Stripe -> Client : {source-id} + Stripe -> Client : {sourceId} deactivate Stripe - Client -> "client-api": POST /profile {source-id, email} + Client -> "client-api": POST /profile {sourceId} activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note - "client-api" -> "payment-processor" : Create new profile {source-id, email} + "client-api" -> "payment-processor" : createProfile(name, sourceId) activate "payment-processor" - "payment-processor" -> Stripe : POST /v1/customers {email} (Create new subscriber with Stripe) - activate Stripe - "Stripe" -> "payment-processor" : {customer-id} + alt successful case + "payment-processor" -> Stripe : POST /v1/customers {name} (Create new subscriber with Stripe) + activate Stripe + "Stripe" -> "payment-processor" : {customerId} - "payment-processor" -> Stripe : POST /v1/customers/{customer-id}/sources {source-id} - "Stripe" -> "payment-processor" - deactivate Stripe + "payment-processor" -> Stripe : POST /v1/customers/{customerId}/sources {sourceId} + "Stripe" -> "payment-processor" + deactivate Stripe - "payment-processor" -> Storage : Store profile {customer-id, source-id} - activate Storage - Storage -> "payment-processor" : {profile-id} + "payment-processor" -> Storage : createProfile(name, customerId, sourceId) + activate Storage + Storage -> "payment-processor" : {result} - "payment-processor" -> Storage : Get default subscription plan - Storage -> "payment-processor" : {plan-id} - deactivate Storage + "payment-processor" -> Storage : getDefaultPlan(name) + Storage -> "payment-processor" : {planId} + deactivate Storage - "payment-processor" -> Stripe : POST v1/subscriptions {plan-id, customer-id} - activate Stripe - "Stripe" -> "payment-processor" - deactivate Stripe + "payment-processor" -> Stripe : POST v1/subscriptions {planId}, {customerId} + activate Stripe + "Stripe" -> "payment-processor" + deactivate Stripe + + else error + note right of "payment-processor" : Unroll charge with Stripe etc. (TBD) + + end "payment-processor" -> OCS : Notify OCS From bf16e7e8a7fa20ecf63fa2650662ace940a9f63a Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 11:15:16 +0200 Subject: [PATCH 045/118] Minor updates to 'new-subscriber' and 'purchase' flow diagrams --- .../doc/puml/create-new-subscriber.puml | 14 +++++++------- .../doc/puml/purchase-with-new-source.puml | 4 ++++ .../doc/puml/purchase-with-saved-source.puml | 4 ++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/payment-processor/doc/puml/create-new-subscriber.puml b/payment-processor/doc/puml/create-new-subscriber.puml index 6f3ba094a..f03752b6a 100644 --- a/payment-processor/doc/puml/create-new-subscriber.puml +++ b/payment-processor/doc/puml/create-new-subscriber.puml @@ -7,7 +7,7 @@ box "Prime" participant "client-api" participant "payment-processor" participant OCS - participant Storage + participant DAO end box participant Stripe @@ -38,13 +38,13 @@ activate Client "Stripe" -> "payment-processor" deactivate Stripe - "payment-processor" -> Storage : createProfile(name, customerId, sourceId) - activate Storage - Storage -> "payment-processor" : {result} + "payment-processor" -> DAO : createProfile(name, customerId) + activate DAO + DAO -> "payment-processor" : {result} - "payment-processor" -> Storage : getDefaultPlan(name) - Storage -> "payment-processor" : {planId} - deactivate Storage + "payment-processor" -> DAO : getDefaultPlan(name) + DAO -> "payment-processor" : {planId} + deactivate DAO "payment-processor" -> Stripe : POST v1/subscriptions {planId}, {customerId} activate Stripe diff --git a/payment-processor/doc/puml/purchase-with-new-source.puml b/payment-processor/doc/puml/purchase-with-new-source.puml index 0ddbf8338..60eaa64f5 100644 --- a/payment-processor/doc/puml/purchase-with-new-source.puml +++ b/payment-processor/doc/puml/purchase-with-new-source.puml @@ -23,6 +23,10 @@ activate Client Client -> "client-api": POST /products {sku, sourceId, saveCardFlag} activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note "client-api" -> "payment-processor" : purchaseProduct(name, sku, sourceId, saveCardFlag) activate "payment-processor" diff --git a/payment-processor/doc/puml/purchase-with-saved-source.puml b/payment-processor/doc/puml/purchase-with-saved-source.puml index 0f0c3e7a4..cb632a27d 100644 --- a/payment-processor/doc/puml/purchase-with-saved-source.puml +++ b/payment-processor/doc/puml/purchase-with-saved-source.puml @@ -24,6 +24,10 @@ activate Client Client -> "client-api": POST /products {sku, sourceId} activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note "client-api" -> "payment-processor" : purchaseProduct(name, sku, sourceId) activate "payment-processor" From e3a2f9a5942e1b74c9941248a6007656617bb837 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 11:36:27 +0200 Subject: [PATCH 046/118] Adds flow diagrams for purchase with default source --- .../doc/puml/create-new-subscriber.puml | 3 +- payment-processor/doc/puml/list-sources.puml | 4 + .../puml/purchase-with-default-source.puml | 76 +++++++++++++++++++ .../doc/puml/set-default-source.puml | 42 ++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 payment-processor/doc/puml/purchase-with-default-source.puml create mode 100644 payment-processor/doc/puml/set-default-source.puml diff --git a/payment-processor/doc/puml/create-new-subscriber.puml b/payment-processor/doc/puml/create-new-subscriber.puml index f03752b6a..9a18f087a 100644 --- a/payment-processor/doc/puml/create-new-subscriber.puml +++ b/payment-processor/doc/puml/create-new-subscriber.puml @@ -38,9 +38,10 @@ activate Client "Stripe" -> "payment-processor" deactivate Stripe - "payment-processor" -> DAO : createProfile(name, customerId) + "payment-processor" -> DAO : createProfile(name, customerId, sourceId) activate DAO DAO -> "payment-processor" : {result} + note right : {sourceId} will be saved as default payment source "payment-processor" -> DAO : getDefaultPlan(name) DAO -> "payment-processor" : {planId} diff --git a/payment-processor/doc/puml/list-sources.puml b/payment-processor/doc/puml/list-sources.puml index 8637b599d..945f3fa57 100644 --- a/payment-processor/doc/puml/list-sources.puml +++ b/payment-processor/doc/puml/list-sources.puml @@ -13,6 +13,10 @@ participant Stripe activate Client Client -> "client-api": GET /sources activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note "client-api" -> "payment-processor" : getSavedSources(name) activate "payment-processor" diff --git a/payment-processor/doc/puml/purchase-with-default-source.puml b/payment-processor/doc/puml/purchase-with-default-source.puml new file mode 100644 index 000000000..c79e92fbe --- /dev/null +++ b/payment-processor/doc/puml/purchase-with-default-source.puml @@ -0,0 +1,76 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant DAO +end box +participant Stripe + +activate Client + + Client -> Client : Select product ({sku}) + + Client -> "client-api": POST /products {sku} + activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note + + "client-api" -> "payment-processor" : purchaseProduct(name, sku) + activate "payment-processor" + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + "payment-processor" -> DAO : getDefaultSource(name) + activate DAO + DAO -> "payment-processor" : {sourceId} + deactivate DAO + + "payment-processor" -> DAO : getProduct(sku) + activate DAO + DAO -> "payment-processor" : {product} + deactivate DAO + + alt successful case + "payment-processor" -> Stripe : POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description} + activate Stripe + Stripe -> "payment-processor" : {chargeInfo} + deactivate Stripe + + "payment-processor" -> DAO: recordChargeInfo(name, chargeInfo) + activate DAO + DAO -> "payment-processor" : {result} + deactivate DAO + note right + Charges/refunds to be recorded + in a "ledger" type of store + end note + + "payment-processor" -> OCS : updateBucket(name, product.size) + activate OCS + OCS -> "payment-processor" : {result} + deactivate OCS + + else error + note right of "payment-processor" : Unroll charge with Stripe etc. (TBD) + + end + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml diff --git a/payment-processor/doc/puml/set-default-source.puml b/payment-processor/doc/puml/set-default-source.puml new file mode 100644 index 000000000..b56136755 --- /dev/null +++ b/payment-processor/doc/puml/set-default-source.puml @@ -0,0 +1,42 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant "DAO" +end box +participant Stripe + +activate Client + + Client -> "client-api" : Get saved sources + activate "client-api" + "client-api" -> Client : {list of sources} + deactivate "client-api" + note right : See "list-sources" flow diagram + + Client -> "client-api" : PUT /sources {sourceId} + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note + + "client-api" -> "payment-processor" : setDefaultSource(name, sourceId) + + "payment-processor" -> DAO : setDefaultSource(name, sourceId) + activate DAO + DAO -> "payment-processor" : {result} + deactivate DAO + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml From 15ffef8be1ea984f2eba77f46fb731c038e52ac4 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 11:55:49 +0200 Subject: [PATCH 047/118] Minor updates to 'create-source' flow diagram --- payment-processor/doc/puml/create-source.puml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/payment-processor/doc/puml/create-source.puml b/payment-processor/doc/puml/create-source.puml index a387d823c..a16842fb6 100644 --- a/payment-processor/doc/puml/create-source.puml +++ b/payment-processor/doc/puml/create-source.puml @@ -20,8 +20,14 @@ activate Client deactivate Stripe Client -> "client-api" : POST /sources {sourceId} + activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note "client-api" -> "payment-processor" : addSource(name, sourceId) + activate "payment-processor" "payment-processor" -> DAO : getStripeCustomerId(name) activate DAO From c22eb1e7063019b6b5e5e22e2c8d5f7f637350ca Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 12:04:39 +0200 Subject: [PATCH 048/118] Adds flow diagram for deleting a subscription --- .../doc/puml/delete-subscription.puml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 payment-processor/doc/puml/delete-subscription.puml diff --git a/payment-processor/doc/puml/delete-subscription.puml b/payment-processor/doc/puml/delete-subscription.puml new file mode 100644 index 000000000..80858d3d0 --- /dev/null +++ b/payment-processor/doc/puml/delete-subscription.puml @@ -0,0 +1,57 @@ +@startuml + +actor Client +participant Client + +box "Prime" + participant "client-api" + participant "payment-processor" + participant OCS + participant DAO +end box +participant Stripe + +activate Client + + Client -> "client-api": DELETE /profile + activate "client-api" + note right of "client-api" + {name} identifies the user (from Oauth2 auth.) + and is equivalent to the users email address + end note + + "client-api" -> "payment-processor" : deleteProfile(name) + activate "payment-processor" + + "payment-processor" -> DAO : getStripeCustomerId(name) + activate DAO + DAO -> "payment-processor" : {customerId} + deactivate DAO + + alt successful case + "payment-processor" -> Stripe : DELETE /v1/customers {customerId} + activate Stripe + "Stripe" -> "payment-processor" : {result} + deactivate Stripe + + "payment-processor" -> DAO : deleteProfile(name) + activate DAO + DAO -> "payment-processor" : {result} + deactivate DAO + + else error + note right of "payment-processor" : Unroll etc. (TBD) + + end + + "payment-processor" -> OCS : Clear bucket etc. + + "payment-processor" -> "client-api" : {result} + deactivate "payment-processor" + + "client-api" -> Client : {result} + deactivate "client-api" + +deactivate Client + +@enduml From bb94bb9a8e89fbe10083da7021099cf1bd9a7be7 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Fri, 13 Jul 2018 12:12:52 +0200 Subject: [PATCH 049/118] Updates 'svg' files for flow diagrams --- .../doc/diagrams/create-new-subscriber.svg | 83 ++++++++++++++ .../doc/diagrams/create-plan.svg | 53 +++++++++ .../doc/diagrams/create-product.svg | 48 ++++++++ .../doc/diagrams/create-source.svg | 63 +++++++++++ .../doc/diagrams/delete-subscription.svg | 70 ++++++++++++ .../doc/diagrams/list-sources.svg | 56 ++++++++++ .../doc/diagrams/purchase-flow.svg | 81 -------------- .../doc/diagrams/purchase-state-diagram.svg | 27 ----- .../diagrams/purchase-with-default-source.svg | 89 +++++++++++++++ .../doc/diagrams/purchase-with-new-source.svg | 104 ++++++++++++++++++ .../diagrams/purchase-with-saved-source.svg | 91 +++++++++++++++ .../doc/diagrams/set-default-source.svg | 55 +++++++++ 12 files changed, 712 insertions(+), 108 deletions(-) create mode 100644 payment-processor/doc/diagrams/create-new-subscriber.svg create mode 100644 payment-processor/doc/diagrams/create-plan.svg create mode 100644 payment-processor/doc/diagrams/create-product.svg create mode 100644 payment-processor/doc/diagrams/create-source.svg create mode 100644 payment-processor/doc/diagrams/delete-subscription.svg create mode 100644 payment-processor/doc/diagrams/list-sources.svg delete mode 100644 payment-processor/doc/diagrams/purchase-flow.svg delete mode 100644 payment-processor/doc/diagrams/purchase-state-diagram.svg create mode 100644 payment-processor/doc/diagrams/purchase-with-default-source.svg create mode 100644 payment-processor/doc/diagrams/purchase-with-new-source.svg create mode 100644 payment-processor/doc/diagrams/purchase-with-saved-source.svg create mode 100644 payment-processor/doc/diagrams/set-default-source.svg diff --git a/payment-processor/doc/diagrams/create-new-subscriber.svg b/payment-processor/doc/diagrams/create-new-subscriber.svg new file mode 100644 index 000000000..e48691ff5 --- /dev/null +++ b/payment-processor/doc/diagrams/create-new-subscriber.svg @@ -0,0 +1,83 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeCollecting payment informationCreate Source{sourceId}POST /profile {sourceId}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresscreateProfile(name, sourceId)alt[successful case]POST /v1/customers {name} (Create new subscriber with Stripe){customerId}POST /v1/customers/{customerId}/sources {sourceId}createProfile(name, customerId, sourceId){result}{sourceId} will be saved as default payment sourcegetDefaultPlan(name){planId}POST v1/subscriptions {planId}, {customerId}[error]Unroll charge with Stripe etc. (TBD)Notify OCS{result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/create-plan.svg b/payment-processor/doc/diagrams/create-plan.svg new file mode 100644 index 000000000..187de25c2 --- /dev/null +++ b/payment-processor/doc/diagrams/create-plan.svg @@ -0,0 +1,53 @@ +PrimeAdminAdminadmin-apiadmin-apipayment-processorpayment-processorStorageStorageStripeStripePOST /plans {sku}POST /plans {sku}Read product {sku}{prod-id} (e.g. Stripe prod-id)POST /v1/plans {prod-id} (create plan with Stripe){plan-id}Save plan as a subscription {plan-id}{subscription-id}{subscription-id}{subscription-id} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/create-product.svg b/payment-processor/doc/diagrams/create-product.svg new file mode 100644 index 000000000..0bd8b78f8 --- /dev/null +++ b/payment-processor/doc/diagrams/create-product.svg @@ -0,0 +1,48 @@ +PrimeAdminAdminadmin-apiadmin-apipayment-processorpayment-processorStorageStorageStripeStripePOST /products (create product)POST /products (create product)POST /v1/products (create product){prod-id}Save product {prod-id}{sku}{sku}{sku} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/create-source.svg b/payment-processor/doc/diagrams/create-source.svg new file mode 100644 index 000000000..96d31cb90 --- /dev/null +++ b/payment-processor/doc/diagrams/create-source.svg @@ -0,0 +1,63 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeCollecting payment informationCreate new source with Stripe{sourceId}POST /sources {sourceId}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addressaddSource(name, sourceId)getStripeCustomerId(name){customerId}POST /v1/customers/{customerId}/sources {sourceId}{sourceInfo}{result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/delete-subscription.svg b/payment-processor/doc/diagrams/delete-subscription.svg new file mode 100644 index 000000000..18e3e70d5 --- /dev/null +++ b/payment-processor/doc/diagrams/delete-subscription.svg @@ -0,0 +1,70 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeDELETE /profile{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addressdeleteProfile(name)getStripeCustomerId(name){customerId}alt[successful case]DELETE /v1/customers {customerId}{result}deleteProfile(name){result}[error]Unroll etc. (TBD)Clear bucket etc.{result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/list-sources.svg b/payment-processor/doc/diagrams/list-sources.svg new file mode 100644 index 000000000..410e145ae --- /dev/null +++ b/payment-processor/doc/diagrams/list-sources.svg @@ -0,0 +1,56 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorDAODAOStripeStripeGET /sources{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addressgetSavedSources(name)getStripeCustomerId(name){customerId}GET /v1/customers/<customerId>{customer}The {customer} object includes a list of sources[{source}, ... ]{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-flow.svg b/payment-processor/doc/diagrams/purchase-flow.svg deleted file mode 100644 index ca16af1dd..000000000 --- a/payment-processor/doc/diagrams/purchase-flow.svg +++ /dev/null @@ -1,81 +0,0 @@ -PrimeClientClientclient-apiclient-apiOCSOCSStorageStoragepayment-processorpayment-processorStripeStripeCollecting payment informationCheckout{token}POST /products {SKU, token}State { Purchase Initiated }Perform paymentCharge {token}Charge Object {id,result..}State { Payment Completed }Notify OCSUpdate BalanceState { Balance Updated }Save Purchase Record{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-state-diagram.svg b/payment-processor/doc/diagrams/purchase-state-diagram.svg deleted file mode 100644 index 4fa772bc1..000000000 --- a/payment-processor/doc/diagrams/purchase-state-diagram.svg +++ /dev/null @@ -1,27 +0,0 @@ -PurchaseInitiatedPaymentCompletedRollbackBalanceUpdatedSuccessFailed \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-with-default-source.svg b/payment-processor/doc/diagrams/purchase-with-default-source.svg new file mode 100644 index 000000000..32d1f42c1 --- /dev/null +++ b/payment-processor/doc/diagrams/purchase-with-default-source.svg @@ -0,0 +1,89 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeSelect product ({sku})POST /products {sku}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresspurchaseProduct(name, sku)getStripeCustomerId(name){customerId}getDefaultSource(name){sourceId}getProduct(sku){product}alt[successful case]POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description}{chargeInfo}recordChargeInfo(name, chargeInfo){result}Charges/refunds to be recordedin a "ledger" type of storeupdateBucket(name, product.size){result}[error]Unroll charge with Stripe etc. (TBD){result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-with-new-source.svg b/payment-processor/doc/diagrams/purchase-with-new-source.svg new file mode 100644 index 000000000..6b140ec7a --- /dev/null +++ b/payment-processor/doc/diagrams/purchase-with-new-source.svg @@ -0,0 +1,104 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeCollecting payment informationCreate new source with Stripe{sourceId}Select product ({sku})POST /products {sku, sourceId, saveCardFlag}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresspurchaseProduct(name, sku, sourceId, saveCardFlag)getStripeCustomerId(name){customerId}getProduct(sku){product}alt[successful case]POST /v1/customers/{customerId}/sources {sourceId}{sourceInfo}Attach new source to customer ({sourceId, customerId})POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description}{chargeInfo}alt[saveCardFlag is false]DELETE /v1/customer/{customerId}/sources/{sourceId}{result)}recordChargeInfo(name, chargeInfo){result}Charges/refunds to be recordedin a "ledger" type of storeupdateBucket(name, product.size){result}[error]Unroll charge with Stripe etc. (TBD){result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-with-saved-source.svg b/payment-processor/doc/diagrams/purchase-with-saved-source.svg new file mode 100644 index 000000000..fcb1a697e --- /dev/null +++ b/payment-processor/doc/diagrams/purchase-with-saved-source.svg @@ -0,0 +1,91 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeGet saved sources{list of sources}See "list-sources" flow diagramChoose source for payment ({sourceId})Select product ({sku})POST /products {sku, sourceId}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresspurchaseProduct(name, sku, sourceId)getStripeCustomerId(name){customerId}getProduct(sku){product}alt[successful case]POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description}{chargeInfo}recordChargeInfo(name, chargeInfo){result}Charges/refunds to be recordedin a "ledger" type of storeupdateBucket(name, product.size){result}[error]Unroll charge with Stripe etc. (TBD){result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/set-default-source.svg b/payment-processor/doc/diagrams/set-default-source.svg new file mode 100644 index 000000000..d964ff61c --- /dev/null +++ b/payment-processor/doc/diagrams/set-default-source.svg @@ -0,0 +1,55 @@ +PrimeClientClientclient-apiclient-apipayment-processorpayment-processorDAODAOStripeStripeGet saved sources{list of sources}See "list-sources" flow diagramPUT /sources {sourceId}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresssetDefaultSource(name, sourceId)setDefaultSource(name, sourceId){result}{result}{result} \ No newline at end of file From b0a94160c1bb1c0951fcb0d2f5c8b5733baf3d5d Mon Sep 17 00:00:00 2001 From: Martin Cederlof Date: Fri, 13 Jul 2018 14:02:14 +0200 Subject: [PATCH 050/118] Adding list sources to the API This is just a placeholder to what the actual API will be. --- .../client/api/resources/PaymentResource.kt | 2 +- prime/infra/prime-client-api.yaml | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt index ea1570292..14c804b75 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/PaymentResource.kt @@ -14,7 +14,7 @@ import javax.ws.rs.core.Response * Payment API. * */ -@Path("/payment") +@Path("/paymentSources") class PaymentResource(private val dao: SubscriberDAO) : ResourceHelpers() { private val paymentProcessor by lazy { getResource() } diff --git a/prime/infra/prime-client-api.yaml b/prime/infra/prime-client-api.yaml index ea70cccd2..13eb9bcba 100644 --- a/prime/infra/prime-client-api.yaml +++ b/prime/infra/prime-client-api.yaml @@ -89,6 +89,21 @@ paths: description: "Not able to store token." security: - auth0_jwt: [] + "/paymentSources": + get: + description: "Get all payment sources for the user." + produces: + - application/json + operationId: "listSources" + responses: + 200: + description: "List of payment sources." + schema: + $ref: '#/definitions/PaymentSourceList' + 404: + description: "No user found." + security: + - auth0_jwt: [] "/products": get: description: "Get all products for the user." @@ -258,6 +273,31 @@ definitions: required: - sku - price + PaymentSourceList: + type: array + items: + $ref: '#/definitions/PaymentSource' + PaymentSource: + type: object + properties: + id: + desctiption: "The identifier for the source" + type: string + exp_month: + description: "Expiry Month" + type: integer + exp_year: + description: "Expiry Year" + type: integer + last4: + description: "Last for digit from card number" + type: string + brand: + description: "card branch" + type: string + object: + description: "String representing the object’s type" + type: string ConsentList: type: array items: From 3c6a68727ea5926c9cc80deb8109eda3c55127c7 Mon Sep 17 00:00:00 2001 From: "Kjell M. Myksvoll" Date: Mon, 16 Jul 2018 13:19:22 +0200 Subject: [PATCH 051/118] Moves fetch of 'payment-id' from 'payment' module to 'client-api' --- .../doc/diagrams/create-new-subscriber.svg | 61 ++++++++++++------- .../doc/diagrams/create-source.svg | 14 ++--- .../doc/diagrams/delete-subscription.svg | 34 +++++++---- .../doc/diagrams/list-sources.svg | 14 ++--- .../doc/diagrams/purchase-state-diagram.svg | 27 ++++++++ .../diagrams/purchase-with-default-source.svg | 26 ++++---- .../doc/diagrams/purchase-with-new-source.svg | 28 ++++----- .../diagrams/purchase-with-saved-source.svg | 22 +++---- .../doc/diagrams/set-default-source.svg | 14 ++--- .../doc/puml/create-new-subscriber.puml | 59 +++++++++++------- payment-processor/doc/puml/create-source.puml | 12 ++-- .../doc/puml/delete-subscription.puml | 32 ++++++---- payment-processor/doc/puml/list-sources.puml | 12 ++-- .../puml/purchase-with-default-source.puml | 24 ++++---- .../doc/puml/purchase-with-new-source.puml | 26 ++++---- .../doc/puml/purchase-with-saved-source.puml | 20 +++--- .../doc/puml/set-default-source.puml | 12 ++-- 17 files changed, 255 insertions(+), 182 deletions(-) create mode 100644 payment-processor/doc/diagrams/purchase-state-diagram.svg diff --git a/payment-processor/doc/diagrams/create-new-subscriber.svg b/payment-processor/doc/diagrams/create-new-subscriber.svg index e48691ff5..35c0cd7b1 100644 --- a/payment-processor/doc/diagrams/create-new-subscriber.svg +++ b/payment-processor/doc/diagrams/create-new-subscriber.svg @@ -1,4 +1,4 @@ -PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeCollecting payment informationCreate Source{sourceId}POST /profile {sourceId}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresscreateProfile(name, sourceId)alt[successful case]POST /v1/customers {name} (Create new subscriber with Stripe){customerId}POST /v1/customers/{customerId}/sources {sourceId}createProfile(name, customerId, sourceId){result}{sourceId} will be saved as default payment sourcegetDefaultPlan(name){planId}POST v1/subscriptions {planId}, {customerId}[error]Unroll charge with Stripe etc. (TBD)Notify OCS{result}{result} \ No newline at end of file diff --git a/payment-processor/doc/diagrams/purchase-with-default-source.svg b/payment-processor/doc/diagrams/purchase-with-default-source.svg index 32d1f42c1..b344c311c 100644 --- a/payment-processor/doc/diagrams/purchase-with-default-source.svg +++ b/payment-processor/doc/diagrams/purchase-with-default-source.svg @@ -1,4 +1,4 @@ -PrimeClientClientclient-apiclient-apipayment-processorpayment-processorOCSOCSDAODAOStripeStripeSelect product ({sku})POST /products {sku}{name} identifies the user (from Oauth2 auth.)and is equivalent to the users email addresspurchaseProduct(name, sku)getStripeCustomerId(name){customerId}getDefaultSource(name){sourceId}getProduct(sku){product}alt[successful case]POST /v1/charges {customerId, sourceId, product.amount, product.currency, product.description}{chargeInfo}recordChargeInfo(name, chargeInfo){result}Charges/refunds to be recordedin a "ledger" type of storeupdateBucket(name, product.size){result}[error]Unroll charge with Stripe etc. (TBD){result}{result} + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file From 3084fccc45c1fa88e9b79b4af3ca63c647f157df Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 21 Aug 2018 21:38:36 +0200 Subject: [PATCH 117/118] Temporarily added legacy purchase operation without payment --- .../kotlin/org/ostelco/at/jersey/Tests.kt | 40 +++++++++++++++++++ .../kotlin/org/ostelco/at/okhttp/Tests.kt | 30 ++++++++++++-- .../client/api/resources/ProductsResource.kt | 26 +++++++----- .../prime/client/api/store/SubscriberDAO.kt | 3 ++ .../client/api/store/SubscriberDAOImpl.kt | 27 +++++++++++++ 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt index d2f46a436..c09cfe832 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/jersey/Tests.kt @@ -266,6 +266,46 @@ class PurchaseTest { assert(Instant.now().toEpochMilli() - purchaseRecords.last().timestamp < 10_000) { "Missing Purchase Record" } assertEquals(expectedProducts().first(), purchaseRecords.last().product, "Incorrect 'Product' in purchase record") } + + @Test + fun `jersey test - POST products purchase without payment`() { + + val email = "purchase-legacy-${randomInt()}@test.com" + createProfile(name = "Test Legacy Purchase User", email = email) + + val subscriptionStatusBefore: SubscriptionStatus = get { + path = "/subscription/status" + subscriberId = email + } + val balanceBefore = subscriptionStatusBefore.remaining + + val productSku = "1GB_249NOK" + + post { + path = "/products/$productSku" + subscriberId = email + } + + Thread.sleep(100) // wait for 100 ms for balance to be updated in db + + val subscriptionStatusAfter: SubscriptionStatus = get { + path = "/subscription/status" + subscriberId = email + } + val balanceAfter = subscriptionStatusAfter.remaining + + assertEquals(1_000_000_000, balanceAfter - balanceBefore, "Balance did not increased by 1GB after Purchase") + + val purchaseRecords: PurchaseRecordList = get { + path = "/purchases" + subscriberId = email + } + + purchaseRecords.sortBy { it.timestamp } + + assert(Instant.now().toEpochMilli() - purchaseRecords.last().timestamp < 10_000) { "Missing Purchase Record" } + assertEquals(expectedProducts().first(), purchaseRecords.last().product, "Incorrect 'Product' in purchase record") + } } class AnalyticsTest { diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt index fff2c1e9c..d572624e8 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/okhttp/Tests.kt @@ -76,8 +76,6 @@ class ProfileTest { class GetSubscriptions { - private val logger by logger() - @Test fun `okhttp test - GET subscriptions`() { @@ -167,8 +165,6 @@ class GetProductsTest { class PurchaseTest { - private val logger by logger() - @Test fun `okhttp test - POST products purchase`() { @@ -196,6 +192,32 @@ class PurchaseTest { assert(Instant.now().toEpochMilli() - purchaseRecords.last().timestamp < 10_000) { "Missing Purchase Record" } assertEquals(expectedProducts().first(), purchaseRecords.last().product, "Incorrect 'Product' in purchase record") } + + @Test + fun `okhttp test - POST products purchase without payment`() { + + val email = "purchase-legacy-${randomInt()}@test.com" + createProfile(name = "Test Legacy Purchase User", email = email) + + val client = clientForSubject(subject = email) + + val balanceBefore = client.subscriptionStatus.remaining + + client.buyProductDeprecated("1GB_249NOK") + + Thread.sleep(200) // wait for 200 ms for balance to be updated in db + + val balanceAfter = client.subscriptionStatus.remaining + + assertEquals(1_000_000_000, balanceAfter - balanceBefore, "Balance did not increased by 1GB after Purchase") + + val purchaseRecords = client.purchaseHistory + + purchaseRecords.sortBy { it.timestamp } + + assert(Instant.now().toEpochMilli() - purchaseRecords.last().timestamp < 10_000) { "Missing Purchase Record" } + assertEquals(expectedProducts().first(), purchaseRecords.last().product, "Incorrect 'Product' in purchase record") + } } class ConsentTest { diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/ProductsResource.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/ProductsResource.kt index f0c787ac3..47be01e8d 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/ProductsResource.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/resources/ProductsResource.kt @@ -38,14 +38,21 @@ class ProductsResource(private val dao: SubscriberDAO) { @POST @Path("{sku}") @Produces("application/json") - fun purchaseProductOld(@Auth token: AccessTokenPrincipal?, - @NotNull - @PathParam("sku") - sku: String, - @QueryParam("sourceId") - sourceId: String?, - @QueryParam("saveCard") - saveCard: Boolean = false): Response = purchaseProduct(token, sku, sourceId, saveCard) + fun purchaseProductWithoutPayment(@Auth token: AccessTokenPrincipal?, + @NotNull + @PathParam("sku") + sku: String): Response { + if (token == null) { + return Response.status(Response.Status.UNAUTHORIZED) + .build() + } + + return dao.purchaseProductWithoutPayment(token.name, sku) + .fold( + { apiError -> Response.status(apiError.status).entity(asJson(apiError.description)) }, + { productInfo -> Response.status(CREATED).entity(productInfo) } + ).build() + } @POST @Path("{sku}/purchase") @@ -66,8 +73,7 @@ class ProductsResource(private val dao: SubscriberDAO) { return dao.purchaseProduct(token.name, sku, sourceId, saveCard) .fold( { apiError -> Response.status(apiError.status).entity(asJson(apiError.description)) }, - { productInfo -> Response.status(CREATED).entity(productInfo)} + { productInfo -> Response.status(CREATED).entity(productInfo) } ).build() - } } diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt index 326c9fdc3..0975c490d 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAO.kt @@ -87,4 +87,7 @@ interface SubscriberDAO { && !appToken.tokenType.isEmpty()) } } + + @Deprecated(message = "use purchaseProduct") + fun purchaseProductWithoutPayment(subscriberId: String, sku: String): Either } diff --git a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt index d31b0ee94..34d933132 100644 --- a/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt +++ b/client-api/src/main/kotlin/org/ostelco/prime/client/api/store/SubscriberDAOImpl.kt @@ -26,6 +26,7 @@ import org.ostelco.prime.paymentprocessor.core.ProfileInfo import org.ostelco.prime.paymentprocessor.core.SourceInfo import org.ostelco.prime.storage.ClientDataSource import java.time.Instant +import java.util.* import java.util.concurrent.ConcurrentHashMap /** @@ -186,6 +187,32 @@ class SubscriberDAOImpl(private val storage: ClientDataSource, private val ocsSu } + @Deprecated("use purchaseProduct", ReplaceWith("purchaseProduct")) + override fun purchaseProductWithoutPayment(subscriberId: String, sku: String): Either { + return getProduct(subscriberId, sku) + // If we can't find the product, return not-found + .mapLeft { NotFoundError("Product unavailable") } + .flatMap { product -> + product.sku = sku + val purchaseRecord = PurchaseRecord( + id = UUID.randomUUID().toString(), + product = product, + timestamp = Instant.now().toEpochMilli()) + // Create purchase record + storage.addPurchaseRecord(subscriberId, purchaseRecord) + .mapLeft { storeError -> + logger.error("failed to save purchase record, for subscriberId $subscriberId, sku $sku") + BadGatewayError(storeError.message) + } + // Notify OCS + .flatMap { + //TODO: Handle errors (when it becomes available) + ocsSubscriberService.topup(subscriberId, sku) + Either.right(Unit) + } + } + } + override fun purchaseProduct(subscriberId: String, sku: String, sourceId: String?, saveCard: Boolean): Either { return getProduct(subscriberId, sku) // If we can't find the product, return not-found From a63442a9c0be0a377418b8ae11397e84d7bee29b Mon Sep 17 00:00:00 2001 From: Vihang Patil Date: Tue, 21 Aug 2018 22:38:25 +0200 Subject: [PATCH 118/118] Fixed broken data consumption logic due to missing bundleId in OcsEvent --- README.md | 2 +- acceptance-tests/build.gradle | 2 +- .../main/kotlin/org/ostelco/at/pgw/OcsTest.kt | 114 +++++++++--------- admin-api/build.gradle | 2 +- analytics/build.gradle | 2 +- app-notifier/build.gradle | 2 +- auth-server/build.gradle | 2 +- build.gradle | 2 +- client-api/build.gradle | 2 +- diameter-stack/build.gradle | 2 +- diameter-test/build.gradle | 2 +- ext-auth-provider/build.gradle | 2 +- firebase-store/build.gradle | 2 +- model/build.gradle | 2 +- neo4j-store/build.gradle | 2 +- ocs/build.gradle | 2 +- .../ostelco/prime/events/EventProcessor.kt | 4 +- .../kotlin/org/ostelco/prime/ocs/OcsState.kt | 8 +- payment-processor/build.gradle | 2 +- prime-api/build.gradle | 2 +- prime-client-api/build.gradle | 2 +- prime/build.gradle | 2 +- pseudonym-server/build.gradle | 2 +- tools/neo4j-admin-tools/build.gradle | 2 +- 24 files changed, 88 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 4661aea19..804ce931b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Kotlin version badge](https://img.shields.io/badge/kotlin-1.2.60-blue.svg)](http://kotlinlang.org/) +[![Kotlin version badge](https://img.shields.io/badge/kotlin-1.2.61-blue.svg)](http://kotlinlang.org/) [![Prime version](https://img.shields.io/github/tag/ostelco/ostelco-core.svg)](https://github.com/ostelco/ostelco-core/tags) [![GitHub license](https://img.shields.io/github/license/ostelco/ostelco-core.svg)](https://github.com/ostelco/ostelco-core/blob/master/LICENSE) diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index e6e1001c0..b6689a927 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" } diff --git a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt index 6a4468927..29297d83f 100644 --- a/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt +++ b/acceptance-tests/src/main/kotlin/org/ostelco/at/pgw/OcsTest.kt @@ -1,7 +1,6 @@ package org.ostelco.at.pgw import org.jdiameter.api.Avp -import org.jdiameter.api.AvpDataException import org.jdiameter.api.Session import org.junit.After import org.junit.Before @@ -11,9 +10,12 @@ import org.ostelco.at.common.createProfile import org.ostelco.at.common.createSubscription import org.ostelco.at.common.logger import org.ostelco.at.common.randomInt +import org.ostelco.at.jersey.get import org.ostelco.diameter.model.RequestType import org.ostelco.diameter.test.TestClient import org.ostelco.diameter.test.TestHelper +import org.ostelco.prime.client.model.SubscriptionStatus +import java.lang.Thread.sleep import kotlin.test.assertEquals import kotlin.test.fail @@ -57,22 +59,17 @@ class OcsTest { waitForAnswer() - try { - assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) - val resultAvps = client.resultAvps ?: fail("Missing AVPs") - assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) - assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) - assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) - val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) - assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) - assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).unsigned32) - assertEquals(10, resultMSCC.grouped.getAvp(Avp.RATING_GROUP).unsigned32) - val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) - assertEquals(BUCKET_SIZE, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) - } catch (e: AvpDataException) { - logger.error("Failed to get Result-Code", e) - } - + assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) + val resultAvps = client.resultAvps ?: fail("Missing AVPs") + assertEquals(RequestType.INITIAL_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).unsigned32) + assertEquals(10, resultMSCC.grouped.getAvp(Avp.RATING_GROUP).unsigned32) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(BUCKET_SIZE, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) } private fun simpleCreditControlRequestUpdate(session: Session) { @@ -91,20 +88,25 @@ class OcsTest { waitForAnswer() - try { - assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) - val resultAvps = client.resultAvps ?: fail("Missing AVPs") - assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) - assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) - assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) - val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) - assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) - val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) - assertEquals(BUCKET_SIZE, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) - } catch (e: AvpDataException) { - logger.error("Failed to get Result-Code", e) - } + assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) + val resultAvps = client.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.UPDATE_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) + assertEquals(BUCKET_SIZE, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) + } + + private fun getBalance(): Long { + sleep(200) // wait for 200 ms for balance to be updated in db + val subscriptionStatus: SubscriptionStatus = get { + path = "/subscription/status" + subscriberId = EMAIL + } + return subscriptionStatus.remaining } @Test @@ -114,7 +116,10 @@ class OcsTest { val session = client.createSession() ?: fail("Failed to create session") simpleCreditControlRequestInit(session) + assertEquals(INITIAL_BALANCE - BUCKET_SIZE, getBalance(), message = "Incorrect balance after init") + simpleCreditControlRequestUpdate(session) + assertEquals(INITIAL_BALANCE - 2 * BUCKET_SIZE, getBalance(), message = "Incorrect balance after update") val request = client.createRequest( DEST_REALM, @@ -128,22 +133,19 @@ class OcsTest { waitForAnswer() - try { - assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) - val resultAvps = client.resultAvps ?: fail("Missing AVPs") - assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) - assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) - assertEquals(RequestType.TERMINATION_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) - val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) - assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) - assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).unsigned32) - assertEquals(10, resultMSCC.grouped.getAvp(Avp.RATING_GROUP).unsigned32) - val validTime = resultMSCC.grouped.getAvp(Avp.VALIDITY_TIME) - assertEquals(86400L, validTime.unsigned32) - } catch (e: AvpDataException) { - logger.error("Failed to get Result-Code", e) - } - + assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) + val resultAvps = client.resultAvps ?: fail("Missing AVPs") + assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) + assertEquals(DEST_REALM, resultAvps.getAvp(Avp.ORIGIN_REALM).utF8String) + assertEquals(RequestType.TERMINATION_REQUEST.toLong(), resultAvps.getAvp(Avp.CC_REQUEST_TYPE).integer32.toLong()) + val resultMSCC = resultAvps.getAvp(Avp.MULTIPLE_SERVICES_CREDIT_CONTROL) + assertEquals(2001L, resultMSCC.grouped.getAvp(Avp.RESULT_CODE).integer32.toLong()) + assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).unsigned32) + assertEquals(10, resultMSCC.grouped.getAvp(Avp.RATING_GROUP).unsigned32) + val validTime = resultMSCC.grouped.getAvp(Avp.VALIDITY_TIME) + assertEquals(86400L, validTime.unsigned32) + + assertEquals(INITIAL_BALANCE - 2 * BUCKET_SIZE, getBalance(), message = "Incorrect balance after terminate") } @@ -165,7 +167,7 @@ class OcsTest { waitForAnswer() - try { + run { assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) val resultAvps = client.resultAvps ?: fail("Missing AVPs") assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) @@ -176,12 +178,11 @@ class OcsTest { assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).integer32.toLong()) val granted = resultMSCC.grouped.getAvp(Avp.GRANTED_SERVICE_UNIT) assertEquals(0L, granted.grouped.getAvp(Avp.CC_TOTAL_OCTETS).unsigned64) - } catch (e: AvpDataException) { - logger.error("Failed to get Result-Code", e) } - // There is 2 step in graceful shutdown. First OCS send terminate, then P-GW report used units in a final update + assertEquals(INITIAL_BALANCE, getBalance(), message = "Incorrect balance after init using wrong msisdn") + val updateRequest = client.createRequest( DEST_REALM, DEST_HOST, @@ -194,7 +195,7 @@ class OcsTest { waitForAnswer() - try { + run { assertEquals(2001L, client.resultCodeAvp?.integer32?.toLong()) val resultAvps = client.resultAvps ?: fail("Missing AVPs") assertEquals(DEST_HOST, resultAvps.getAvp(Avp.ORIGIN_HOST).utF8String) @@ -205,10 +206,9 @@ class OcsTest { assertEquals(1, resultMSCC.grouped.getAvp(Avp.SERVICE_IDENTIFIER_CCA).integer32.toLong()) val validTime = resultMSCC.grouped.getAvp(Avp.VALIDITY_TIME) assertEquals(86400L, validTime.unsigned32) - } catch (e: AvpDataException) { - logger.error("Failed to get Result-Code", e) } + assertEquals(INITIAL_BALANCE, getBalance(), message = "Incorrect balance after update using wrong msisdn") } @@ -234,18 +234,20 @@ class OcsTest { private const val DEST_REALM = "loltel" private const val DEST_HOST = "ocs" + private const val INITIAL_BALANCE = 100_000_000L private const val BUCKET_SIZE = 500L + private lateinit var EMAIL: String private lateinit var MSISDN: String @BeforeClass @JvmStatic fun createTestUserAndSubscription() { - val email = "ocs-${randomInt()}@test.com" - createProfile(name = "Test OCS User", email = email) + EMAIL = "ocs-${randomInt()}@test.com" + createProfile(name = "Test OCS User", email = EMAIL) - MSISDN = createSubscription(email) + MSISDN = createSubscription(EMAIL) } } } diff --git a/admin-api/build.gradle b/admin-api/build.gradle index 2d47ecfa3..5cf370b1c 100644 --- a/admin-api/build.gradle +++ b/admin-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/analytics/build.gradle b/analytics/build.gradle index b5d95091f..857e271a9 100644 --- a/analytics/build.gradle +++ b/analytics/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" id "idea" diff --git a/app-notifier/build.gradle b/app-notifier/build.gradle index 47e4d97dd..4489a3fd8 100644 --- a/app-notifier/build.gradle +++ b/app-notifier/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/auth-server/build.gradle b/auth-server/build.gradle index 745d299b3..0c5ed3979 100644 --- a/auth-server/build.gradle +++ b/auth-server/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" id "idea" diff --git a/build.gradle b/build.gradle index ac40020fa..bb6b15cd6 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ subprojects { options.encoding = 'UTF-8' } ext { - kotlinVersion = "1.2.60" + kotlinVersion = "1.2.61" dropwizardVersion = "1.3.5" googleCloudVersion = "1.35.0" jacksonVersion = "2.9.6" diff --git a/client-api/build.gradle b/client-api/build.gradle index 9254d4fdb..c41c249d3 100644 --- a/client-api/build.gradle +++ b/client-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/diameter-stack/build.gradle b/diameter-stack/build.gradle index 91dad811d..4651ff9b9 100644 --- a/diameter-stack/build.gradle +++ b/diameter-stack/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" id "signing" id "maven" diff --git a/diameter-test/build.gradle b/diameter-test/build.gradle index 582af7372..01daf992a 100644 --- a/diameter-test/build.gradle +++ b/diameter-test/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" id "signing" id "maven" diff --git a/ext-auth-provider/build.gradle b/ext-auth-provider/build.gradle index 0d8f80a9e..06474ae34 100644 --- a/ext-auth-provider/build.gradle +++ b/ext-auth-provider/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" } diff --git a/firebase-store/build.gradle b/firebase-store/build.gradle index d40e8a4e7..9da55192a 100644 --- a/firebase-store/build.gradle +++ b/firebase-store/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/model/build.gradle b/model/build.gradle index 58250515f..f634e4d5f 100644 --- a/model/build.gradle +++ b/model/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/neo4j-store/build.gradle b/neo4j-store/build.gradle index c893da5e7..8ebf3be0e 100644 --- a/neo4j-store/build.gradle +++ b/neo4j-store/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/ocs/build.gradle b/ocs/build.gradle index f8fcbde6a..979532b84 100644 --- a/ocs/build.gradle +++ b/ocs/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/ocs/src/main/kotlin/org/ostelco/prime/events/EventProcessor.kt b/ocs/src/main/kotlin/org/ostelco/prime/events/EventProcessor.kt index caf264c25..59f8fc6a0 100644 --- a/ocs/src/main/kotlin/org/ostelco/prime/events/EventProcessor.kt +++ b/ocs/src/main/kotlin/org/ostelco/prime/events/EventProcessor.kt @@ -3,6 +3,7 @@ package org.ostelco.prime.events import com.lmax.disruptor.EventHandler import org.ostelco.prime.disruptor.EventMessageType.CREDIT_CONTROL_REQUEST import org.ostelco.prime.disruptor.EventMessageType.RELEASE_RESERVED_BUCKET +import org.ostelco.prime.disruptor.EventMessageType.REMOVE_MSISDN_TO_BUNDLE_MAPPING import org.ostelco.prime.disruptor.EventMessageType.TOPUP_DATA_BUNDLE_BALANCE import org.ostelco.prime.disruptor.OcsEvent import org.ostelco.prime.logger @@ -28,7 +29,8 @@ class EventProcessor( try { if (event.messageType == CREDIT_CONTROL_REQUEST || event.messageType == RELEASE_RESERVED_BUCKET - || event.messageType == TOPUP_DATA_BUNDLE_BALANCE) { + || event.messageType == TOPUP_DATA_BUNDLE_BALANCE + || event.messageType == REMOVE_MSISDN_TO_BUNDLE_MAPPING) { logger.info("Updating data bundle balance for {} : {} to {} bytes", event.msisdn, event.bundleId, event.bundleBytes) val bundleId = event.bundleId diff --git a/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsState.kt b/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsState.kt index e3bc9ca2b..b44a14084 100644 --- a/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsState.kt +++ b/ocs/src/main/kotlin/org/ostelco/prime/ocs/OcsState.kt @@ -45,7 +45,8 @@ class OcsState(val loadSubscriberInfo:Boolean = true) : EventHandler { event.reservedBucketBytes = reserveDataBytes( msisdn, event.requestedBucketBytes) - event.bundleBytes = getDataBundleBytes(msisdn = msisdn) + event.bundleId = msisdnToBundleIdMap[msisdn] + event.bundleBytes = bundleBalanceMap[event.bundleId] ?: 0 } TOPUP_DATA_BUNDLE_BALANCE -> { val bundleId = event.bundleId @@ -63,6 +64,8 @@ class OcsState(val loadSubscriberInfo:Boolean = true) : EventHandler { return } releaseReservedBucket(msisdn = msisdn) + event.bundleId = msisdnToBundleIdMap[msisdn] + event.bundleBytes = bundleBalanceMap[event.bundleId] ?: 0 } UPDATE_BUNDLE -> { val bundleId = event.bundleId @@ -98,9 +101,10 @@ class OcsState(val loadSubscriberInfo:Boolean = true) : EventHandler { logger.error("Received null as bundleId") return } + releaseReservedBucket(msisdn = msisdn) + event.bundleBytes = bundleBalanceMap[bundleId] ?: 0 msisdnToBundleIdMap.remove(msisdn) bundleIdToMsisdnMap[bundleId]?.remove(msisdn) - // TODO vihang: return reserved bytes back to bundle } } } catch (e: Exception) { diff --git a/payment-processor/build.gradle b/payment-processor/build.gradle index 39132ed07..799aef9fa 100644 --- a/payment-processor/build.gradle +++ b/payment-processor/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" id "idea" } diff --git a/prime-api/build.gradle b/prime-api/build.gradle index b02f947a5..9f95213f2 100644 --- a/prime-api/build.gradle +++ b/prime-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "java-library" } diff --git a/prime-client-api/build.gradle b/prime-client-api/build.gradle index e7c0e52e2..ad07f35e9 100644 --- a/prime-client-api/build.gradle +++ b/prime-client-api/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id 'java-library' id 'org.hidetake.swagger.generator' version '2.12.0' id "idea" diff --git a/prime/build.gradle b/prime/build.gradle index aef25759c..6178de08b 100644 --- a/prime/build.gradle +++ b/prime/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" id "idea" diff --git a/pseudonym-server/build.gradle b/pseudonym-server/build.gradle index 6aa4ce110..3b17980cd 100644 --- a/pseudonym-server/build.gradle +++ b/pseudonym-server/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" id "idea" diff --git a/tools/neo4j-admin-tools/build.gradle b/tools/neo4j-admin-tools/build.gradle index d7a0c09bd..362e4206b 100644 --- a/tools/neo4j-admin-tools/build.gradle +++ b/tools/neo4j-admin-tools/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.jetbrains.kotlin.jvm" version "1.2.60" + id "org.jetbrains.kotlin.jvm" version "1.2.61" id "application" id "com.github.johnrengelman.shadow" version "2.0.4" id "idea"