Table of Contents
Overview of Roku Pay
The core services of Roku Pay are the billing of customers for subscriptions and the management of invoicing. You can bill customers within your Roku channel for a subscription or prior to installing your channel. You must create your channel prior to monetizing it. For developers new to Roku development, see the overview on Monetization.
In-Channel Purchasing Work Flow
In-channel purchasing can be processed and verified programmatically through the SceneGraph control node ChannelStore and the Roku Web Service API. The sample described in this document uses ChannelStore to show a simple sign up scenario, not the entire in-channel purchasing workflow and not the use of the Web Service API. This document describes the code to implement purchasing during sign up.
The following diagram shows the use of ChannelStore and Web Service API to implement in-channel purchasing.
Create, then Monetize a Channel
You can monetize a public or non-certified channel. Before you can monetize your new channel, you need to create it using Developer Dashboard and then write the code. Your new channel can be a public channel, but it does not need to be published or certified prior to adding monetization.
Creating a New Channel
If you are creating a new channel, follow the steps shown below. You will need a catalog feed for your content so, if you have not created a feed yet, you can use one from a Roku sample.
Go through the development process for creating a public channel, but do not publish or certify it. Later, once you have tested it and monetized it, you can certify and publish it.
Pre-requisites:
Create a Roku customer account: my.roku.com/signup
- Upgrade to a Roku developer account: developer.roku.com/enrollment/standard
- Enroll in the Roku Partner Payouts Program to receive payments for channels, games, content, and ads: developer.roku.com/developer/billing
- Create a public channel using SceneGraph, including creating or using a catalog of content (see the channel tutorial for a sample feed)
- Then package it but do not publish it or get it certified.
- Monetize in Developer Dashboard.
Testing a Channel
Once you have updated your channel to include the code necessary to implement billing, you'll want to test it. For this, navigate to the Developer Dashboard, find the channel, and designate it as the "billing testing" channel. For detailed instructions on how to test in-channel billing implementations, see Testing In-Channel Purchasing.
Note: Be sure to register anyone (including yourself) who will be testing transactions in your channel as a Test User before testing the new billing code, otherwise they will be charged for the test transactions they make.
Purchasing in the Sign Up Sample
Sample Application: ChannelStore_SignupFlow.zip
The ChannelStore_SignupFlow sample shows the code you need to implement to sign up a new user and allow the purchase of a subscription using the SceneGraph ChannelStore object.
Side-load this sample channel to see the sign up screens, including the purchase of a subscription. You might be asked for your Roku PIN, so have it available.
1- Once the channel is installed, launch it and you will see the screen for login or sign up. This sample only handles sign up, not login, so choose "New subscriber (sign up)."
2- Selecting "New subscriber (sign up)" brings up the "Request for information" screen. Enter the testing account’s email address and click "Allow" to use.
3- Enter a password and click "Continue."
4- If you see the example "Terms Of Use" screen, select "Accept" to display the products associated with the channel.
5- The "Subscription" screen lists the products available for purchase. Choose a product.
6- Enter your Roku PIN to confirm the purchase.
7- Once the purchase is confirmed, the in-channel purchase is complete.
How the ChannelStore_SignupFlow Sample Works
The ChannelStore_SignupFlow sample's package contains:
- main.brs file typical for SceneGraph channels.
- other typical SceneGraph folders, such as /components containing the custom components for the sample and /images for the splash screens used as backgrounds for the dialogs.
- custom component HomeScene to control the dialogs and flow as the user moves through the sample.
- mockup APIs so the sample doesn't store any data in the Roku device's persistent memory. In
API.brs
the mockups are described with information on to how to implement them in a real channel. - /docs directory that contains additional documentation about the sample. (Be sure you remove the /docs folder from any real channel you might create based on this sample.)
As with all Roku chanels, the sample's flow of control begins in main.brs
when the user launches the channel. The main.brs file calls the API object, defined in API.brs, to use mockup APIs for the sake of simplifying this sample. Main also creates the component HomeScene to control the other components. HomeScene gathers results from other components to control the flow of the sample and display the dialogs as the user moves through the sample.
RokuSignUp Component
In this example of in-channel purchasing a custom component, RokuSignUp, controls the user sign up and purchase of a subscription as defined in RokuSignUp.xml
.
RokuSignUp.xml
The RokuSignUp component is a SceneGraph node written to implement the standard Roku billing sign up flow. It allows interaction with the channel-specific API for user sign up (login is not shown in this sample). All user interaction dialogs and their generic properties are customizable using interface fields in the RokuSignUp API as defined in RokuSignUp.xml.
You can customize the interface fields for your own channel.
Two script commands in RokuSignUp.xml
link to the BrightScript code:
<script type="text/brightscript" uri="pkg:/components/RokuSignUp/RokuSignUp.brs"/> <script type="text/brightscript" uri="pkg:/components/RokuSignUp/utils.brs"/> |
RokuSignUp.brs
The file RokuSignUp.brs
contains the key purchasing code, including custom functions and fields. The core APIs for in-channel purchasing are the ChannelStore commands:
getPurchases
getUserData
getCatalog
doOrder
The billing
Object
The sample's RokuSignUp.xml
file defines a child ChannelStore
object with the ID billing
.
The RokuSignUp.brs
file includes several ObserveField
commands for billing
. These set up handlers to watch for changes to properties of the billing
component, such as "userData"
. The changes will be triggered by the handlers in RokuSignUp.brs
as the user progresses through the sign up process.
m.billing = m.top.FindNode("billing") m.billing.ObserveField("userData", "On_billing_partialUserData") m.billing.ObserveField("catalog", "On_billing_catalog") m.billing.ObserveField("purchases", "On_billing_purchases") m.billing.ObserveField("orderStatus", "On_billing_purchaseResult") |
For instance, the userData
field changes in the handler On_billing_partialUserData()
and the user data (the email in the sample) is retrieved when the user clicks "Allow".
sub On_billing_partialUserData() userEmail = "" if m.billing.userData <> invalid then userEmail = m.billing.userData.email end if m.signupAuthFlow.kbdialogEmail.text = userEmail m.signupAuthFlow.show = true end sub |
Sample's Runtime Flow of Control
At launch, the flow through the sample uses the custom RokuSignUp component to check if there is an active Roku subscription for the account, gather the user's name, email and password for a new account, locate the purchasing products available in the channel, and sign up the user for the product.
As you examine the code in RokuSignUp.brs
, you will find:
- The function
GetParsedProducts()
returns the list of products available for purchase. - The last ChannelStore command for the RokuSignUp component is
doOrder
, which places the order.
m.billing.command = "doOrder" |
On_billing_products()
sets the fieldisSubscribed
to true when the user has successfully signed up.- The
isSubscribed
field is handled bymain.brs
to finalize the transaction.
The following table shows which screens in the sample are driven by which commands in RokuSignUp.
Event | Command to use | What it does | Screen in sample |
---|---|---|---|
Launch Channel | In main.brs and other components. In RokuSignUp: m.top.visible | Launches channel and draws UI. Identifies the channel. RokuSignUp sets up handlers. | New subscriber (sign up) |
Identifies Account and gets current entitlements and purchased products | Checks for active Roku subscription for the account:
| ||
Request User Info | Requests name and email to sign up. Get the password for the new account. | ||
Get Catalog | getCatalog | Shows products available for purchase in the channel. | |
Purchase | doOrder | Checks that order is valid, places order. | |
Entitle | Web Service API | In a real channel, the channel would now validate the user request on the channel's backend using the metadata contained in the automated Web Service API push notification returned for the transaction. | (Not shown in sample) |
Show Content | main.brs | Field Content is displayed. | Subscribed successfully |
Code Examples in RokuSignUp
The following examples of code demonstrate key billing functionality through the use of the core APIs for in-channel purchasing, the ChannelStore commands:
Using getPurchases
getPurchases
is the handler for processing the catalog result. The catalog field is a content node containing the command completion status. If successful, the catalog field's ContentNode will have a child ContentNode containing information about each available in-channel purchasing product.
getPurchases
issues a request for the list of purchases associated with the current user's account is issued.
sub On_billing_catalog() if m.billing.catalog = invalid OR m.billing.catalog.status = invalid then m.top.isSubscribed = false return end if
if m.billing.catalog.status = 1 then m.billing.command = "getPurchases" else m.top.isSubscribed = false end if
end sub |
Using getUserData
getUserData
is the handler for getting the user's email.
sub On_dialogSelectSub_buttonSelected() if GetParentScene() = invalid then return end if if m.top.dialogSelectSub.buttonSelected < 0 then On_SubscriptionFailed() else m.billing.requestedUserData = "email" m.billing.command = "getUserData" end if end sub |
Using getCatalog
getCatalog()
is called from On_Signup()
, which is the handler for processing button selection.
sub On_Signup() if GetParentScene() = invalid then return end if
m.parentScene.dialog = m.top.pdialogCheckSubs GetCatalog() end sub |
Then the getCatalog()
handler requests the list of in-channel products that are linked to the channel using getCatalog
.
sub GetCatalog() print ">> Getting Products (Catalog and Purchases)" m.billing.command = "getCatalog" end sub |
Using doOrder
doOrder
places the order for the in-channel purchasing product specified by "indexPurchase". Notice that order
is cleared by setting it to invalid prior to setting myOrder
.
sub PurchaseProduct(index As Integer) ' clear order m.billing.order = invalid
myOrder = CreateObject("roSGNode", "ContentNode") myFirstItem = myOrder.createChild("ContentNode") myFirstItem.addFields({ "code": m.billingProducts.availForPurchase.list[index].code, "qty": 1})
m.billing.order = myOrder
m.billing.command = "doOrder" end sub |
Field Values for RokuSignUp
The following fields are used in the custom RokuSignUp component, grouped as WRITE-ONLY, READ-ONLY and OBSERVE-ONLY.
WRITE-ONLY Fields:
Field | Type | Default | Use |
isAuthNeeded | boolean | false | WRITE-ONLY. Specifies usage of channel-specific API on sign up or login. True = use channel API (see also "isLoginAPISuccess", "isSignupAPISuccess"), this will enable sign up or login selection screen; False = use Roku Billing only. |
regexEmail | string | ^[A-Za-z0-9_%+-]+(\.[A-Za-z0-9_%+-]+)*@([A-Za-z0-9-]+\.)+[A-Za-z]{2,6}$ | WRITE-ONLY. Specifies regular expression for email address validation. |
regexPassword | string | .{1,} | WRITE-ONLY. Specifies regular expression for password validation. |
textTermsOfUse | string | WRITE-ONLY. Specifies the text of Terms-Of-Use agreement for related dialog (see dialogTermsOfUse). If empty string, the Terms-Of-Use dialog is not used and not shown. | |
dialogsConfig | assocarray | WRITE-ONLY. Sets generic attributes for all component dialogs. | |
dialogsButtonConfig | assocarray | WRITE-ONLY. Sets generic attributes for all buttons of component dialogs. | |
show | boolean | false | WRITE-ONLY. Shows or hides the component on the scene. True = show and start sign up flow; False = hide. |
isLoginAPISuccess | boolean | WRITE-ONLY. Use only if isAuthNeeded = true. Allows to set result of channel-specific login API. Used for interaction with the Main thread where API call should be implemented (see also "loginUserData"). True = user is successfully logged in via channel API; False = failed to log in via API. Invokes setting "isAuthorized" and "isAfterLoginAuthFlow" fields. | |
isSignupAPISuccess | boolean | WRITE-ONLY. Use only if isAuthNeeded = true. Allows to set result of channel-specific sign up API. Used for interaction with the Main thread where API call should be implemented (see also "signupUserData"). True = user is successfully signed up via channel API (means user account was created and user was logged in with it); False = failed to sign up via API. Invokes setting "isAuthorized" and "isAfterLoginAuthFlow" fields. |
READ-ONLY Fields:
Field | Type | Default | Use |
posterSplash | node | READ-ONLY. Provides access to background splash Poster. | |
buttonLoginFlow | node | READ-ONLY. Provides access to login flow selection Button. | |
buttonSignupFlow | node | READ-ONLY. Provides access to sign up flow selection Button. | |
kbdialogLoginEmail | node | READ-ONLY. Provides access to login flow email address entry KeyboardDialog. Should have 2 buttons, they will be used for "Continue" and "Back" actions respectively. | |
kbdialogLoginPassword | node | READ-ONLY. Provides access to login flow password entry KeyboardDialog. Should have 3 buttons, they will be used for "Show/hide password", "Continue" and "Back" actions respectively. | |
dialogLoginErrEmail | node | READ-ONLY. Provides access to login flow email address validation error message Dialog. Should have one button, it will be used for confirmation ("OK") action. | |
dialogLoginErrPassword | node | READ-ONLY. Provides access to login flow password validation error message Dialog. Should have one button, it will be used for confirmation ("OK") action. | |
pdialogLogin | node | READ-ONLY. Provides access to login operation ProgressDialog. | |
dialogLoginFailed | node | READ-ONLY. Provides access to login failure message Dialog. Should have 2 buttons, they will be used for "Try again" and "Cancel" actions respectively. | |
kbdialogSignupEmail | node | READ-ONLY. Provides access to sign up flow email address entry KeyboardDialog. Should have 2 buttons, they will be used for "Continue" and "Back" actions respectively. | |
kbdialogSignupPassword | node | READ-ONLY. Provides access to sign up flow password entry KeyboardDialog. Should have 3 buttons, they will be used for "Show/hide password", "Continue" and "Back" actions respectively. | |
dialogSignupErrEmail | node | READ-ONLY. Provides access to sign up flow email address validation error message Dialog. Should have one button, it will be used for confirmation ("OK") action. | |
dialogSignupErrPassword | node | READ-ONLY. Provides access to sign up flow password validation error message Dialog. Should have one button, it will be used for confirmation ("OK") action. | |
pdialogSignup | node | READ-ONLY. Provides access to sign up operation ProgressDialog. | |
dialogSignupFailed | node | READ-ONLY. Provides access to sign up failure message Dialog. Should have 2 buttons, they will be used for "Try again" and "Cancel" actions respectively. | |
dialogTermsOfUse | node | READ-ONLY. Provides access to Terms-Of-Use message Dialog. Should have 2 buttons, they will be used for "Accept" and "Decline" actions respectively. If dialog's "message" field is empty string then Terms-Of-Use dialog is not used and not shown (see also "textTermsOfUse") | |
dialogSelectSub | node | READ-ONLY. Provides access to Roku Billing subscription selection Dialog. Buttons will be replaced with actual subscription products. | |
dialogNoSubToPurchase | node | READ-ONLY. Provides access to error message Dialog indicating that there are no available Roku Billing subscriptions to purchase, and user needs to check their Roku account for previously purchased products. Should have one button, it will be used for confirmation ("OK") action. | |
dialogBillingNA | node | READ-ONLY. Provides access to error message Dialog indicating that Roku Billing subscription service is currently unavailable. Should have one button, it will be used for confirmation ("OK") action. | |
pdialogCheckSubs | node | READ-ONLY. Provides access to Roku Billing subscription checking operation ProgressDialog | |
pdialogPurchase | node | READ-ONLY. Provides access to Roku Billing purchase operation ProgressDialog | |
isAuthorized | boolean | false | READ-ONLY. Valid only if channel-specific API is used on sign up or login (see "isAuthNeeded", "isLoginAPISuccess", "isSignupAPISuccess"). Provides the authorization flow (sign up or login) result. True = user was successfully authorized; False = not authorized. Flow type (login or sign up) is stored in "isAfterLoginAuthFlow" field. |
OBSERVE-ONLY Fields:
Field | Type | Default | Use |
---|---|---|---|
loginUserData | assocarray | OBSERVE-ONLY. Valid only if channel-specific API is used on login/sign up (see "isAuthNeeded"). Set when user data is obtained from login flow. Used to pass user data to the Main thread where login API call should be implemented (see also isLoginAPISuccess). | |
signupUserData | assocarray | OBSERVE-ONLY. Valid only if channel-specific API is used on login/sign up (see "isAuthNeeded"). Set when user data is obtained from signup flow. Used to pass user data to the Main thread where sign up API call should be implemented (see also isLoginAPISuccess) | |
isAfterLoginAuthFlow | boolean | OBSERVE-ONLY. Valid only if channel-specific API is used on login/sign up (see "isAuthNeeded"). Set when user authorization flow (login/sign up) is complete, and specifies its type: login or sign up. True = login flow; False = sign up flow. The flow result (whether user was authorized via API) is stored in "isAuthorized" field. | |
isSubscribed | boolean | false | OBSERVE-ONLY. Set when user billing subscription flow is complete. True = success (user subscribed); False = failure (user not subscribed). |
Attachments:
inchanprodaddprod.png (image/png)
addinchanprod.png (image/png)
manageinchanprods.png (image/png)
devhomebilling.png (image/png)
pubchanedit.png (image/png)
removechannel1.png (image/png)
useraddchannel.png (image/png)
privchansubmitted.png (image/png)
privchannelsubmitgreen.png (image/png)
privchanneldescripsfilled.png (image/png)
privchanneldescrips.png (image/png)
privchannelprops.png (image/png)
pubchanapproved.png (image/png)
pubchancancelsubmit.png (image/png)
pubchansubmitgreen.png (image/png)
pubchansubmit.png (image/png)
channelscreenshots.png (image/png)
addpubchandescripts.png (image/png)
addpubchanpropsoptions.png (image/png)
addpubchanpropsfilled.png (image/png)
addpubchanprops.png (image/png)
managechannelshome.png (image/png)
developerhome.png (image/png)
rokubillinghome.png (image/png)
rokudeveloperhome.png (image/png)
rokulinkdevice.png (image/png)
rokuaccountoptions.png (image/png)
rokuaccounthome.png (image/png)
package_inspector.png (application/octet-stream)
Roku Integrated Billing April 2013.pdf (application/octet-stream)
worddave038f3aa81ed3d32e44695053fbfedfb.png (application/octet-stream)
pkg_pkg.jpg (application/octet-stream)
pkg_nopkg.jpg (application/octet-stream)
inst_nokey.jpg (application/octet-stream)
noapp.jpg (application/octet-stream)
inst_nokey2.jpg (application/octet-stream)
noapp2.jpg (application/octet-stream)
inst2.jpg (application/octet-stream)
util_nofile.jpg (application/octet-stream)
util_nofile2.jpg (application/octet-stream)
pkg_pkg2.jpg (application/octet-stream)
pkg_nopkg2.jpg (application/octet-stream)
worddav9caaf5ac4211488b5913dada2a43fa04.png (application/octet-stream)
worddav7ad197e62b92bd644ece5b26d095a34d.png (application/octet-stream)
worddavef7a7053263247e25a246589cc62d2e7.png (application/octet-stream)
worddav64163c6ade0c1cb109caab0eff053bac.png (application/octet-stream)
worddav88b05c81f7ada13fc983542701f6e328.png (application/octet-stream)
worddavbe46dcd86d2cce6625d24cf54cb82f34.png (application/octet-stream)
worddav4a433c5106afa644e4b374c7180fdd08.png (application/octet-stream)
worddav28d1f1b7cb141db20a75167c01070147.png (application/octet-stream)
worddavedc2ae8c8293e8363fe16ec328e70101.png (application/octet-stream)
worddav7dba05cfe8d89fa7f5baa3db042c1e8d.png (application/octet-stream)
worddav2a2701ca78126a3f829650e64921631b.png (application/octet-stream)
worddav2cf5d5dbe889d37e8a8147ecc3685f64.png (application/octet-stream)
inst.jpg (application/octet-stream)
worddav78d201bc04a4e2fc05f70bcc1391e7ce.png (application/octet-stream)
worddav39c597cf739fd1979a29c23e6c018c16.png (application/octet-stream)
worddavd34c5fc68ffd697aff0f7463cc9538f5.png (application/octet-stream)
worddavb4cdf653c7167d21f934fcb142989fd9.png (application/octet-stream)
worddav65b700e317517ab8daf315d8282b4aaf.png (application/octet-stream)
worddavfc4074710ee8080fec6ab35cd6a8c98b.png (application/octet-stream)
worddav356bde7056d214fdff0d30bb2770005c.png (application/octet-stream)
worddav81b0e2cd76f584a9e514d27b7edf8c19.png (application/octet-stream)
worddav05f2a297492dfa22902e24eec9515cbf.png (application/octet-stream)
worddav282d6bfa7de108e6da6996439c7e7699.png (application/octet-stream)
worddaved33ec0abc417fb400f1a035316bb045.png (application/octet-stream)
worddavd5be699b750fb2040b52ac5915843eef.png (application/octet-stream)
worddavd475a32955fd430a91bdcced43d6f046.png (application/octet-stream)
worddave733f84e5306fc044d52214e658ab7e2.png (application/octet-stream)
worddav53b871ae9447068a7eb4739675c3220f.png (application/octet-stream)
worddavf29e5c023d5a41275e48e4ecb5d11f8a.png (application/octet-stream)
development-application-installer.png (image/png)
putty-telnet.png (image/png)
terminal-telnet.png (image/png)
putty-genkey.png (image/png)
terminal-genkey.png (image/png)
sideloaded-channel.png (image/png)
application-packager.png (image/png)
application-packaged.png (image/png)
add-new-channel.png (image/png)
add-custom-sdk-private-channel.png (image/png)
private-channel-properties.png (image/png)
private-channel-ch-store-info.png (image/png)
private-channel-monetization.png (image/png)
package-upload.png (image/png)
private-channel-preview-publish.png (image/png)
add-product.png (image/png)
manage-products.png (image/png)
roku-billing-guide.jpg (image/jpeg)
create-password.jpg (image/jpeg)
terms-of-use.jpg (image/jpeg)
select-subscription.jpg (image/jpeg)
pay-to-install.png (image/png)
share-info.jpg (image/jpeg)
roku-billing1.png (image/png)
roku-billing2.png (image/png)
roku-billing2.png (image/png)
roku-billing2.png (image/png)
roku-billing2.png (image/png)
roku-billing3.png (image/png)
finalfinal.png (image/png)
yoooo.png (image/png)
please.png (image/png)
roku-channel-billing-flow.pdf (application/pdf)
purchasing_code_Flow.png (image/png)
billing swimlanes.png (image/png)
billing 1.jpg (image/jpeg)
billing 2.png (image/png)
billing 3.jpg (image/jpeg)
billing 4.jpg (image/jpeg)
billing 5.jpg (image/jpeg)
billing 6.jpg (image/jpeg)
runtime flow of control.png (image/png)