Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 49 additions & 15 deletions app/src/main/java/io/token/sample/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
import static com.google.common.base.Charsets.UTF_8;
import static io.grpc.Status.Code.NOT_FOUND;
import static io.token.TokenIO.TokenCluster.SANDBOX;
import static io.token.proto.common.alias.AliasProtos.Alias.Type.EMAIL;
import static io.token.TokenRequest.TokenRequestOptions.REDIRECT_URL;
import static io.token.proto.common.alias.AliasProtos.Alias.Type.DOMAIN;
import static io.token.util.Util.generateNonce;

import com.google.common.io.Resources;
import io.grpc.StatusRuntimeException;
import io.token.Destinations;
import io.token.Member;
import io.token.TokenIO;
import io.token.TokenRequest;
import io.token.TransferTokenBuilder;
import io.token.proto.common.account.AccountProtos.BankAccount;
import io.token.proto.common.account.AccountProtos.BankAccount.Sepa;
import io.token.proto.common.alias.AliasProtos.Alias;
import io.token.proto.common.token.TokenProtos.Token;
import io.token.proto.common.transfer.TransferProtos.Transfer;
Expand All @@ -28,6 +32,7 @@
import java.util.LinkedHashMap;
import java.util.Map;


import spark.Spark;

/**
Expand Down Expand Up @@ -58,22 +63,51 @@ public static void main(String[] args) throws IOException {
// Initializes the server
Spark.port(3000);

// Endpoint for transfer payment, called by client side after user approves payment.
// Endpoint for transfer payment, called by client side to initiate a payment.
Spark.post("/transfer", (req, res) -> {
Map<String, String> formData = parseFormData(req.body());
String tokenId = formData.get("tokenId");

// Make sure to get the token first,
// and check its validity
TransferEndpoint destination = TransferEndpoint.newBuilder()
.setAccount(BankAccount.newBuilder()
.setSepa(Sepa.newBuilder()
.setIban(formData.get("destination"))))
.build();
//create TokenRequest
TransferTokenBuilder tokenBuilder =
new TransferTokenBuilder(
Double.parseDouble(formData.get("amount")),
formData.get("currency"))
.setDescription(formData.get("description"))
.addDestination(destination)
.setToAlias(merchantMember.firstAlias())
.setToMemberId(merchantMember.memberId());

TokenRequest request = TokenRequest.create(tokenBuilder)
.setOption(REDIRECT_URL, "http://localhost:3000/redeem");

String requestId = merchantMember.storeTokenRequest(request);

//generate Token Request URL to redirect to
String tokenRequestUrl = tokenIO.generateTokenRequestUrl(requestId);
//send a 302 Redirect
res.status(302);
res.redirect(tokenRequestUrl);
return null;
});

Spark.get("/redeem", (req, res) -> {
String callbackUri = req.raw().getRequestURL().toString()
+ "?"
+ req.raw().getQueryString();
String tokenId = tokenIO.parseTokenRequestCallbackUrl(callbackUri).getTokenId();
//get the token and check its validity
Token token = merchantMember.getToken(tokenId);

// Redeem the token at the server, to move the funds
Transfer transfer = merchantMember.redeemToken(token, 4.99, "EUR", "example");
return "";
//redeem the token at the server to move the funds
Transfer transfer = merchantMember.redeemToken(token);
res.status(200);
return "Success! Redeemed transfer " + transfer.getId();
});
// (If user closes browser before this function is called, we don't redeem the token.
// Since this function is where we get the shipping information, we probably don't
// want to redeem the token: we wouldn't know where to ship the goods.)

// Serve the web page and JS script:
String script = Resources.toString(Resources.getResource("script.js"), UTF_8)
Expand Down Expand Up @@ -138,10 +172,10 @@ private static Member createMember(TokenIO tokenIO) {
// Generate a random username.
// If we try to create a member with an already-used name,
// it will fail.
String email = "merchant-sample-" + generateNonce().toLowerCase() + "+noverify@example.com";
String domain = "merchant-sample-" + generateNonce().toLowerCase() + ".com";
Alias alias = Alias.newBuilder()
.setType(EMAIL)
.setValue(email)
.setType(DOMAIN)
.setValue(domain)
.build();
return tokenIO.createMember(alias);
// The newly-created member is automatically logged in.
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div id="tokenPayBtn" style="width: 200px"></div>
<button id="tokenPayBtn" style="width: 200px">Token Quick Checkout</button>
<script src="/script.js"></script>
</body>
</html>
60 changes: 24 additions & 36 deletions app/src/main/resources/script.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
function shippingCb(address, tokenCallback) {
tokenCallback({ // Can return price based on address
shippingMethods: [
{
id: '0',
name: 'Standard Ground (5-9 business days)',
deliveryTime: '5 - 9 Business Days',
cost: 0
},
],
tax: 0
});
function initiatePayment() {
var XHR = new XMLHttpRequest();

// Set up our request
XHR.open('POST', 'http://localhost:3000/transfer', true);

XHR.setRequestHeader("Content-Type", "application/json; charset=utf-8");

var data = $.param({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are relying on jQuery for this, why not use $.post instead of native XMLHttpRequest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were problems with the redirect part when I was using $.post, ended up with preflight response errors from the web app. This way didn't cause those errors, could be worth playing with to make it consistent though.

merchantId: 'Merchant 123',
amount: 4.99,
currency: 'EUR',
description: 'Book Purchase',
destination: 'DE16700222000072880129'
});

// Define what happens on successful data submission
XHR.addEventListener("load", function(event) {
window.location.replace(event.target.responseURL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are using the redirect flow here, but you can keep working with the popup, check merchant-demo code: https://github.com/tokenio/merchant-demo/blob/master/src/components/TokenEnablerButton/index.js#L74 it can take the token request url instead of terms, let me know if you need my help :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to demonstrate plain redirect since that's what our documentation shows. I originally tried using the bindButton set up but it caused all sorts of problems and confusion, so figured for the sample we're giving to devs we should keep it as plain as can be, and show that all Token specific stuff can be kept to the backend 😀

});

// Send the data; HTTP headers are set automatically
XHR.send(data);
}

// Initializes the Quick Checkout Button
Token.styleButton({ // Sets up the Quick Checkout button
id: "tokenPayBtn",
label: "Token Quick Checkout"
}).bindPayButton(
{ // Terms
alias: { // Merchant alias
type: 'EMAIL',
value: '{alias}' // (filled in by server)
},
amount: 4.99, // Amount
currency: 'EUR', // Currency
destinations: [{account: {sepa: { iban: "DE16700222000072880129"}}}]
},
shippingCb, // Shipping callback (null for "virtual" goods)
function(data) { // Success callback
$.post(
'http://localhost:3000/transfer',
data);
},
function(error) { // Failure callback
console.log('Something\'s wrong!', error);
}
);
document.getElementById("tokenPayBtn").onclick = initiatePayment;
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ buildscript {

allprojects {
group = 'io.token.sample'
version = '1.0.1'
version = '1.0.2'

project.apply plugin: 'java'
project.apply plugin: 'idea'

ext {
ver = [
sparkjava: '2.6.0',
tokenSdk: '1.0.85',
tokenSdk: '1.0.123',
]
}

Expand Down