Building an online marketplace in Java with WePay and Play
This tutorial will teach you how to build an online marketplace from scratch. Popular marketplaces include Airbnb, Etsy, and eBay.
It includes eight easy steps, and it should take you less than an hour to complete. By the time you’re finished, you’ll have built a sample application that facilitates payments between buyers and sellers.
1. Getting Started
You should follow this tutorial step-by-step. Each section is necessary to complete the sample application. You can download all the finished code for the sample application on Github. At any time you can play with the live demo on Heroku.
We’re going to build WeFarm, a sample Java application that uses the WePay API to help farmers sell produce online (think of it as an online farmers’ market).
The WePay API lets you instantly enable users (in this case, Farmers) to accept payments online. It supports a seamless user-experience on your platform, while shielding your application from fraud and regulatory complexity. It’s the easiest way to support payments in your marketplace.
Learn more about the WePay APIFirst, you’ll need to install and setup the Play framework for Java development. Learn how to get started with Play here. Once you've got Play installed, create a new Play application from the terminal.
$ play new wefarm
We’re also going to use Twitter Bootstrap as our front-end framework, so you should add the Bootstrap files to your project. Change your application's main.css to load Bootstrap and Bootstrap-responsive files in the correct order ("@import bootstrap.min" before "@import bootstrap-responsive.min"), and add some basic styling:
@import bootstrap.min.css; @import bootstrap-responsive.min.css; @media (min-width: 767px) { body { padding-top: 60px; } } a.btn { color: #FFF; } a.btn:visited { color: #FFF; } div.navbar div.container a { color: #EEE }
Now that we've established the structure of our website, let's create a simple homepage. Open main.scala.html, the site's default basic page. You'll notice in the body of the page a "@content" placeholder. This is where other pages of our site will be loaded in. Let's first add the header bar before the @content section so that our header will render on top of each page. Change the body section of the main page.
<body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <a href="@routes.Application.index()">WeFarm</a> <div class="nav-collapse collapse"> <ul class="nav pull-right"> <li> <td>Register</td> </li> <li> <td>Login</td> </li> </ul> </div> </div> </div> <div class="container"> @content </div> </body>
We also need to edit index.scala.html. By default, the home page of the site loads the index page into the @content section of the main page. Let's edit the body of the index page to display a welcome message. We can add the list of farmers later.
@main("WeFarm") { <h1>Welcome to WeFarm</h1> <p>You can buy a lots of fresh and local farm-to-fork produce here. If you are a farmer and want to sell your produce, please register.</p> }
Finally, we need to edit the Application controller's index() method. At present, it renders Play's welcome message. We've built our own welcome page in index.scala.html, so we want to edit the index() function to render our own welcome page.
public static Result index() { return ok(views.html.index.render("")); }
Now, type into your console "play run" from within the project folder. You can now see the welcome page at http://localhost:9000.
2. Models
Most real world web applications have complex models and relationships. However, since this is a basic tutorial, let's simplify the model to have just one object: “farmer.” On the backend, this would be represented by a farmers table. Let's assume for now that each farmer has one farm and sells one type of produce. Now we can store the farmer's name, email, password, farm name, produce and price in a single row of our database.
Create a new class in the models folder called Farmer.java. In this file we will define the attributes of a farmer and implement some basic functions to allow us to create and view farmers.
@Entity public class Farmer extends Model { @Id public Long id; public String email; public String password; public String name; public String farm; public String produce; public BigDecimal produce_price; public String wepay_access_token; public Long wepay_account_id; public Finder<Long,Farmer> find = new Finder(Long.class, Farmer.class); public static List<Farmer> all() { return find.all(); } public static void create(Farmer farmer) { farmer.save(); } }
You'll notice @Entity and @Id assignments to certain parts of the Farmer model. These declarations tell the Play framework that we want to treat farmers as objects in our database and to identify each farmer primarily by his or her id.
Now that we've built our Farmer model, let's go back and add a table to the homepage to display all the farmers on WeFarm. First we'll need to pass the list of all farmers from the controller to the view. Add the list of farmers as an argument in the index method's render call.
public static Result index() { return ok(views.html.index.render(Farmer.all()); }
We'll also add the table to the index view so that all farmers are displayed on the home page. Don't forget to accept the list of farmers as a parameter at the top of the view.
@(farmers: List[Farmer]) @main("WeFarm") { <h1>Welcome to WeFarm</h1> <p>You can buy lots of fresh and local farm-to-fork produce here. If you are a farmer and want to sell your produce, please register.</p> <br> <h3>Our farmers</h3> <table class="table table-striped table-bordered"> <tbody> <tr> <th>Farmer</th> <th>Farm</th> <th>Produce</th> <th>Produce Price</th> </tr> @for(farmer <- farmers) { <tr> <td><a href="@routes.Application.show(farmer.id)"> @farmer.name</a> </td> <td><a href="@routes.Application.show(farmer.id)"> @farmer.farm</a> </td> <td>@farmer.produce</td> <td>$@farmer.produce_price</td> </tr> } </tbody> </table> }
3. Database
A farmers market with no farmers is no fun, so we now need a way to add farmers to WeFarm and save them in a database. Play includes a built-in database called H2, but we're going to manage our WeFarm database locally in our own database. If you don't want to connect a database locally and would prefer to use H2, feel free to skip this section. If not, follow along here to learn how to connect WeFarm to a local database.
We'll be using a MySQL database, but any database is compatible with our website. First, if you don't have MySQL downloaded on your computer, download it now. You can also find further instructions there on installing and configuring MySQL on your computer.
Once MySQL is set up, you'll need to modify two files in your project to access the database from within your code. First, in Build.scala, we'll need to add the database to the app's dependencies (there are other dependencies already included, just leave those there and add the connector to mysql).
val appDependencies = Seq( javaCore, javaJdbc, javaEbean, "mysql" % "mysql-connector-java" % "5.1.18" )
Next, we need to edit the application.conf file to load our local database rather than H2 at runtime. Find the Database configuration section of application.conf and edit it appropriately with your database's name and password (you may need to uncomment some of this code).
db.default.driver=com.mysql.jdbc.Driver db.default.url="jdbc:mysql://localhost/wefarm?characterEncoding=UTF-8" db.default.jndiName=DefaultDS db.default.user=yourusername db.default.password="yourpassword" ebean.default="models.*"
Now we can access the database from the controller and manage database requests throughout the code. We'll access it most from the Farmer model, so definitely include this line in that file.
javax.sql.DataSource db = DB.getDataSource();
Finally we'll add a class to the models folder to automatically configure our database on startup of the server. Add a class called MyServerConfigStartup to the models folder with the following code.
import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.event.ServerConfigStartup; public class MyServerConfigStartup implements ServerConfigStartup { @Override public void onStart(ServerConfig serverConfig) { serverConfig.setDatabaseSequenceBatchSize(1); } }
4. Registering and Displaying Farmers
Now our database is connected to the site, so we're ready to handle registration requests. We'll first need to edit the Application controller to handle requests for the registration page and to interpret the form to create new farmers.
public static Result register() { return ok(views.html.register.render(farmerForm)); } public static Result newFarmer() { Form<Farmer> filledForm = farmerForm.bindFromRequest(); if(filledForm.hasErrors()) { return redirect(routes.Application.index()); } else { Farmer.create(filledForm.get()); return redirect(routes.Application.show(filledForm.get().id)); } }
We also need to add the create() method to our Farmer model so that newly created farmers can be saved to the database. We'll want to encrypt their passwords before saving to the database for the sake of security.
public static void create(Farmer farmer) { farmer.save(); }
Now we must build a registration form and create a new view to display the form so farmers can register and join WeFarm. You can build a standard HTML form, but Play includes some powerful techniques to make form building even easier so we'll use Play's form syntax.
@(farmerForm: Form[Farmer]) @main("WeFarm") { <h1>New farmer registration</h1> @form(routes.Application.newFarmer()) { @inputText(farmerForm("name"), '_label -> "Name") @inputText(farmerForm("email"), '_label -> "Email") @inputPassword(farmerForm("password"), '_label -> "Password") @inputText(farmerForm("farm"), '_label -> "Farm") @inputText(farmerForm("produce"), '_label -> "Produce") @inputText(farmerForm("produce_price"), '_label -> "Produce Price") <input type="submit" class="btn btn-primary" value="Create Farmer"> } <a href="@routes.Application.index()">Back</a> }
Finally, we need to edit the routes file to interpret requests for the newly created functions in the controller.
GET /register/ controllers.Application.register() POST /register/ controllers.Application.newFarmer()
We'll want to connect the registration page to the link on the homepage, so edit the header section in main.scala.html to add a link to the registration form to the word "Register."
Now we will build a show page that will render a farmer's information when clicked on from the home page. When farmers complete registration, they'll be redirected here. Later, this page will also present users with an opportunity to buy produce from this farmer, but for now it will just render information. Create a new show() function in the Application controller that takes a farmer's id as an argument to render this farmer's page.
public static Result show(Long id) { return ok(views.html.show.render(farmer); }
We can also build a couple helper functions in the Farmer model to help us find farmers in the database.
public static Farmer findByEmail(String email) { return find.where().eq("email", email).findUnique(); } public static Farmer findById(Long id) { return find.ref(id); }
Next we'll need to create a new view called to template the show page. Create a new view called show.scala.html in the views folder and add the following code. Notice that it takes a farmer as a parameter to display.
@(farmer: Farmer) @import helper._ @main("WeFarm") { <h1 class="floatLeft">@farmer.farm</h1> <p> <b>Name:</b> @farmer.name </p> <p> <b>Email:</b> @farmer.email </p> <p> <b>Farm:</b> @farmer.farm </p> <p> <b>Produce:</b> @farmer.produce </p> <p> <b>Produce price:</b> $@farmer.produce_price </p> }
Finally edit the routes file to incldue a route to the show page that includes the argument to show.
GET /farmers/:id controllers.Application.show(id: Long)
Now if we refresh the site in our browser, we can click on farmers or farms from the home page and render a show page with that farmer's information.
5. User Authentication and Sessions
Farmers can now register and browse the WeFarm site, but we need to add a way for existing users to login and logout. To keep our Application controller neater and our code more organized, we'll created a new Session controller. Create Session.java in the controllers folder. We'll define a login form with a Login subclass and we'll define functions login and authenticate to render and interpret a login form and a logout function to end the session.
public class Session extends Controller { public static class Login { public String email; public String password; public String validate() { if(Farmer.authenticate(email, password) == null) { return "Invalid user or password"; } return null; } } public static Result login() { return ok(views.html.login.render(form(Login.class))); } public static Result authenticate() { Form<Login> loginForm = form(Login.class).bindFromRequest(); if(loginForm.hasErrors()) { return redirect(routes.Session.login()); } else { session("connected", loginForm.get().email); return redirect(routes.Application.index()); } } public static Result logout() { session().clear(); return redirect(routes.Application.index()); } public static boolean isSessionOwner(Farmer farmer) { if (farmer.email.equals(session("connected"))) return true; else return false; } }
We will track session owners by email which is why we set session("connected") to the user's email. You can add that same line to the register function in the Application controller so that when someone registers they become the session owner too. Next we'll create a new view called login.scala.html. We'll design this page much like register.scala.html to allow farmers to login with their email and password.
@(loginForm: Form[Session.Login]) @main("WeFarm") { <h1>Farmer login</h1> @form(routes.Session.authenticate()) { @inputText(farmerForm("email"), '_label -> "Email") @inputPassword(farmerForm("password"), '_label -> "Password") <input type="submit" class="btn btn-primary" value="Login"> } }
We'll also need to add routes to login and logout methods in the routes file like we did for registration.
GET /login/ controllers.Session.login() POST /login/ controllers.Session.authenticate() GET /logout/ controllers.Session.logout()
Finally, we'll want to activate the links in the main header. We'll want to connect the link to login and create a link for logged-in users to logout.
<ul class="nav pull-right"> @if(session().isEmpty()) { <li> <td><a href="@routes.Application.register()">Register</a></td> </li> <li> <td><a href="@routes.Session.login()">Login</a></td> </li> } else { <li> <td><a href="@routes.Session.logout()">Logout</a></td> </li> } </ul>
Now we'll add some password encryption to our registration and login process. We'll use jBCrypt encryption so we'll need to add it to the project build's dependencies and plugin's resolvers. Add jBCrypt to the Build.scala file and plugins.sbt files accordingly.
val appDependencies = Seq( // Add your project dependencies here, javaCore, javaJdbc, javaEbean, "mysql" % "mysql-connector-java" % "5.1.18", "org.mindrot" % "jbcrypt" % "0.3m" )
logLevel := Level.Warn resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" resolvers += "jBCrypt Repository" at "http://repo1.maven.org/maven2/org/" addSbtPlugin("play" % "sbt-plugin" % "2.1.2")
If you're working with eclipse or another IDE, you may need to refresh your project so that the IDE can find the new lib folder with the new jars and add them to the build path. Follow Play's instructions for refreshing your project in the IDE.
Now that we have encryption tools in place, let's add password encryption and password check functions to the Session controller so we can use encryption securely on registration and login.
public static String encryptPassword(String cleanPassword) { if (cleanPassword == null) return null; else return BCrypt.hashpw(cleanPassword, BCrypt.gensalt()); } public static boolean checkPassword(String candidate, String encrypted) { if (candidate == null || encrypted == null) return false; else return BCrypt.checkpw(candidate, encrypted); }
Let's also make a few small modifications to the farmer registration and login processes to use these new encryption functions. We'll want to encrypt farmers' passwords before storing them on the database, so add an encryption line to the create method of the Farmer model. We'll also modify the authenticate method to check passwords against their encryption on the database.
public static void create(Farmer farmer) { farmer.password = Session.encryptPassword(farmer.password); farmer.save(); } public static Farmer authenticate(String email, String password) { Farmer farmer = find.where().eq("email", email).findUnique(); if (Session.checkPassword(password, farmer.password)) return farmer; else return null; }
Now farmers' passwords are never stored insecurely on the database and registration and login processes are securely encrypted.
6. Integrating the WePay API
The WePay API provides a simple RESTful interface to integrate payments into your marketplace. It supports both buyers and sellers, which is critical for applications like WeFarm. You can see the full documentation here. Rather than access the API directly using HTTP requests, we’ll use the WePay Java SDK which can be downloaded from github.
Unzip the download and find the jars in the lib folder. There are three jars included in the download, WePay, GSON, and JSON. (The WePay jar is built using GSON and JSON, so you'll need to include all three in your project to access the API.) Now we'll add the WePay library to our project.
First create a new folder called "lib" under your project's root folder. Play will recognize that the lib folder contains external libraries and upon compilation will adjust the build path as necessary. Next drag and drop the jars into the new lib folder.
If you're working with eclipse or another IDE, you may need to refresh your project so that the IDE can find the new lib folder with the new jars and add them to the build path. Follow Play's instructions for refreshing your project in the IDE.
Now the WePay library has been added to your project. Before making API calls, you’ll need to create an API application on WePay. WePay provides two environments: stage (stage.wepay.com) and production (wepay.com). Stage is used for testing, and production is used when you’re ready to go live. Since this is a test application, create an Application on stage.
After creating your application on WePay, you should see the Client ID and Client Secret for your app under API Keys. Let's add these values to the configuration file. Fill in yourAppClientId and yourAppClientSecret with your application's Client ID and Client Secret. We'll use the stage environment for now, but when it's time to switch to production you'll need to adjust the configuration accordingly.
#WePay App specific information APP_CLIENT_ID = yourAppClientId APP_CLIENT_SECRET = "yourAppClientSecret" USE_STAGE_ENV = true
Let's also add a WePay object to your Application controller and a function to initialize that WePay object with your API keys on the startup of your server. First, add a WePay object to the Application controller.
public static WePay wepay;
Now we'll add functionality that will initialize this object on the startup of your server. We'll need to access the global variables stored in the configuration file and pass these into the initialize call.
import com.typesafe.config.ConfigFactory; import controllers.Application; import com.wepay.WePay; public class MyServerConfigStartup implements ServerConfigStartup { @Override public void onStart(ServerConfig serverConfig) { Long appClientId = ConfigFactory.load().getLong("APP_CLIENT_ID"); String appClientSecret = ConfigFactory.load().getString("APP_CLIENT_SECRET"); Boolean useStageEnv = ConfigFactory.load().getBoolean("USE_STAGE_ENV"); initializeWePayObject(appClientId, appClientSecret, useStageEnv); serverConfig.setDatabaseSequenceBatchSize(1); } public static void initializeWePayObject(Long appClientId, String appClientSecret, boolean useStageEnv) { Application.wepay = new WePay(); Application.wepay.initialize(appClientId, appClientSecret, useStageEnv); } }
Now, when your server starts up, a WePay object will be initialized in the Application controller with your API Keys. Now you'll be able to make API calls from within your code.
7. Enabling Users to Accept Payments
WePay’s OAuth 2.0 flow lets users create a WePay account using a popup on your site. Once your users have provided a name, email, and password, they can start accepting payments instantly.
OAuth 2.0 is an open protocol that allows secure authorization in a simple and standard method from web, mobile and desktop applications. It’s used by popular web services like Facebook and Google. Unlike most services, however, WePay’s OAuth 2.0 implementation lets a user create a new WePay account in addition to authorizing your application to manage it.
According to the WePay API docs, you can enable your users to accept payments in three steps:
- OAuth2: Get the user's authorization to create and manage their WePay account
- Get an access token for the user
- Create a WePay account for the user using the /account/create call.
We'll use the WePay Java SDK to get access tokens for Farmers. You can find further instructions on how to making API calls with the WePay Java SDK in the README file included in the download from Github. Let's use the SDK and add a few methods to the Farmer model to get, store, and return access tokens for farmers through WePay (these methods follow the SDK's pattern for making API calls as you can find in the Github README).
public boolean hasWepayAccessToken() { return this.wepay_access_token != null; } public boolean hasWepayAccount() { return this.wepay_account_id != null; } public String wepayOauth2Authorize() throws IOException, JSONException { OAuth2Data data = new OAuth2Data(); data.redirect_uri = "http://" + Http.Context.current().request().host() + "/farmers/oauth/" + this.id; data.scope = "manage_accounts,collect_payments,view_user,preapprove_payments,send_money"; data.user_email = this.email; data.user_name = this.name; String url = OAuth2.authorize(data, null); return url; } public void wepayOauth2Token(String code) throws IOException, JSONException, WePayException { OAuth2Data data = new OAuth2Data(); data.redirect_uri = "http://" + Http.Context.current().request().host() + "/farmers/oauth/" + this.id; data.code = code; String token = OAuth2.token(data, null); this.wepay_access_token = token; this.save(); }
We used http://yourhostrooturl/farmers/oauth/:id as the redirect URL, so we need to add the OAuth method to handle that page request from the Application controller.
public static Result oauth(Long id, String code) throws IOException, JSONException, WePayException { Farmer farmer = Farmer.findById(id); farmer.wepayOauth2Token(code); return redirect(routes.Application.show(id)); }
Add this route to the routes file to handle /farmers/oauth/ redirects from WePay:
GET /farmers/oauth/:id controllers.Application.oauth(id: Long, code: String)
To give Farmers the best user-experience, we should create their WePay accounts as soon as we receive their access tokens from WePay.
Add a method to createWepayAccount for the user and modify the wepayOAuth2Token method in the Farmer model to create an account when we receive the access token:
public boolean hasWepayAccessToken() { return this.wepay_access_token != null; } public boolean hasWepayAccount() { return this.wepay_account_id != null; } public String wepayOauth2Authorize() throws IOException, JSONException { OAuth2Data data = new OAuth2Data(); data.redirect_uri = "http://" + Http.Context.current().request().host() + "/farmers/oauth/" + this.id; data.scope = "manage_accounts,collect_payments,view_user,preapprove_payments,send_money"; data.user_email = this.email; data.user_name = this.name; String url = OAuth2.authorize(data, null); return url; } public void wepayOauth2Token(String code) throws IOException, JSONException, WePayException { OAuth2Data data = new OAuth2Data(); data.redirect_uri = "http://" + Http.Context.current().request().host() + "/farmers/oauth/" + this.id; data.code = code; String token = OAuth2.token(data, null); this.wepay_access_token = token; this.save(); if (this.hasWepayAccessToken()) this.wepayAccountCreate(); } public void wepayAccountCreate() throws JSONException, IOException, WePayException { if (this.hasWepayAccessToken() && !this.hasWepayAccount()) { AccountData data = new AccountData(); data.name = this.name; data.description = "Farm selling " + this.produce; Account account = Account.create(data, this.wepay_access_token); this.wepay_account_id = account.getAccount_id(); this.save(); } }
That’s it for OAuth2. Now all we have to do is explain to Farmers that they can start accepting payments by creating an account on WePay. Let’s add a link to the WePay OAuth flow on the farmers show page for farmers on their own page so when they log in they can create a WePay account. We'll also have to pass a few boolean parameters to the show view from the Application controller.
@(farmer: Farmer, isSession: Boolean, hasAccount: Boolean) @import helper._ @main("WeFarm") { <h1 class="floatLeft">@farmer.farm</h1> @if(isSession && !hasAccount) { <p> <b>Please create an account to manage your money: </b> <a class="btn btn-primary" href="@farmer.wepayOauth2Authorize()">Click here to create your account</a> </p> } <p> <b>Name:</b> @farmer.name </p> ...the remainder of show page is the same as before... }
As soon as the Farmer clicks the link, they’re taken to a WePay sign up form, which requires a name, email address, and password.
After the user registers on WePay, they’re redirected back to WeFarm. They’re now ready to accept payments!
8. Integrating Checkout
Now that our Farmers are ready to get paid, let’s integrate checkout, so we can charge their customers. We’re also going to add our own app fee to each payment, so we can generate revenue for WeFarm.
WePay supports two ways of collecting payment information. Both are designed to support a seamless user-experience on your platform.
Embedded form
WePay’s iframe checkout is a pre-built checkout form, which handles data validation, PCI compliance, payment confirmation emails, and more.
Custom form
You can also build your own checkout form. We’ll store the payment information and return a unique token that you can use to charge the customer at any time.
Let’s use the embeddable checkout form to save some time.
To charge customers, we'll have to implement two steps:
- Create a checkout object on wePay to define the payment (amount, etc.) using the /checkout/create call.
- Get the customer’s payment information using the checkout_uri returned by the /checkout/create call.
To create a checkout object, make the /checkout/create call using the access_token of the Farmer that’s receiving the payment.
You’ll want to include the following parameters:
Parameter | Description |
account_id | WePay account id of the Farmer. |
short_description | a brief description of the payment. |
type | "GOODS" in our case. |
amount | the amount of the purchase. |
app_fee | The fee that we’ll charge on top of WePay's fees to generate revenue for your site. Lets charge 10% of the produce price. We’ll calculate the appropriate dollar amount on our end. |
fee_payer | "payee" in our case. This means that we’ll be charging the WePay transaction fees to the Farmer, rather than to his customers. |
mode | "iframe" in our case. |
redirect_uri | the url of our payment confirmation page where WePay will redirect the customers after they’ve completed their payment. |
Let's create a wepayCheckoutCreate method in the Farmer model that makes a "/checkout/create" call using the WePay API and return the response.
public Checkout wepayCheckoutCreate(String redirect_uri) throws IOException, JSONException, WePayException { CheckoutData data = new CheckoutData(); data.account_id = this.wepay_account_id; data.short_description = "Produce sold by " + this.farm; data.type = "GOODS"; data.amount = this.produce_price; data.app_fee = this.produce_price.multiply(BigDecimal.valueOf(0.1)); data.fee_payer = "payee"; data.mode = "iframe"; data.redirect_uri = redirect_uri; Checkout checkout = Checkout.create(data, this.wepay_access_token); return checkout; }
We need two new methods in the Application controller. First, a buy method that builds a checkout uri and passes it to its view. Second, a paymentSuccess method that serves as WePay redirect uri for the payment confirmation:
public static Result buy(Long id) throws IOException, JSONException, WePayException { Farmer farmer = Farmer.findById(id); String redirect_uri = "http://" + Http.Context.current().request().host() + "/farmers/payment_success/" + id; Checkout checkout = farmer.wepayCheckoutCreate(redirect_uri); return ok(views.html.buy.render(farmer, checkout.getCheckout_uri())); } public static Result paymentSuccess(Long id, String checkout_id) { Farmer farmer = Farmer.findById(id); return redirect(routes.Application.show(id)); }
WePay's iframe tutorial shows how to link to the WePay javascript library to render the iframe. We’ll use a div tag with id = "wepay-iframe-div" to render the iframe. Just add a new view page called buy where users can enter their payment information and add the new view to the routes file.
@(farmer: Farmer, checkout_uri: String) @import helper._ @main("WeFarm") { <h1 class="floatLeft"> Buy @farmer.produce from @farmer.farm for $@farmer.produce_price </h1> <p><a href="@routes.Application.show(farmer.id)"> Back to @{farmer.name}'s @farmer.farm farm</a> </p> <div id="wepay-iframe-div"> <script type="text/javascript" src="https://www.wepay.com/js/iframe.wepay.js"></script> <script type="text/javascript">WePay.iframe_checkout("wepay-iframe-div", "@checkout_uri");</script> </div> }
GET /farmers/buy/:id controllers.Application.buy(id: Long) GET /farmers/payment_success/:id controllers.Application.paymentSuccess(id: Long, checkout_id: String)
Show a title and buy link on the farmers show page.
@(farmer: Farmer, isSession: Boolean, hasAccount: Boolean) @import helper._ @main("WeFarm") { <h1 class="floatLeft">@farmer.farm</h1> @if(isSession && !hasAccount) { <p> <b>Please create an account to manage your money: </b> <a class="btn btn-primary" href="@farmer.wepayOauth2Authorize()">Click here to create your account</a> </p> } @if(!isSession && hasAccount) { <p> <a class="btn btn-danger btn-large" href="@routes.Application.buy(farmer.id)">Buy @farmer.produce Now!</a> </p> } <p> <b>Name:</b> @farmer.name </p> ...the remainder of show page is the same as before... }
The checkout page with embedded checkout form should look like this:
At this point, you are all set to test an actual checkout. The WePay Testing page lists fake credit card numbers that you can use for testing purposes.
On hitting 'confirm' you should see the success page:
You now have a working checkout experience for your users. Login to stage.wepay.com to see the the checkouts created by your application: