Payments
Overview
After the user completes the authentication process, they have a token. They now pass it to the application to "login", which tells the application which Mobius user account to withdraw MOBI from (the user public key) when a payment is needed. For a web application, the token is generally passed in via a token request parameter. Upon opening the website/loading the application, it checks that the token is valid (within time bounds, etc) and that the account in the token has added the app as a cosigner so it can withdraw MOBI from it.
Walkthrough
Create DApp Instance##
When working with the dapp
object, we need to create a new instance. The same steps were followed in the Accounts Walkthrough .
/**
* @param {string} APP_SECRET_SEED - App developer secret seed
* @param {string} USER_PUBLIC_KEY - User public key
* @returns {Promise}
*/
const dapp = await Mobius.AppBuilder.build(APP_SECRET_SEED, USER_PUBLIC_KEY);
# Takes 2 Parameters
# App Secret Seed - string
# User Public Key - String
# Returns dapp instance
def dapp
@dapp = Mobius::Client::App.new(dapp_secret_seed, user_public_key)
end
# Takes 2 Parameters
# App Secret Seed - string
# User Public Key - String
# Returns dapp instance
dapp = AppBuilder().build(app_secret_seed, user_public_key)
// Takes 2 parameters
// Param $APP_SECRET_SEED - App developer secret seed
// Param $USER_PUBLIC_KEY - User public key
$dapp = new Mobius\Client\App($APP_SECRET_SEED, $USER_PUBLIC_KEY);
Payment Methods##
Once the dapp
has been initialized, payment methods can be called.
Charge###
Charges specified amount from user account and then optionally transfers it from app account to a third party in the same transaction.
/**
* @param {number} amount - Payment amount
* @param {?string} [target_address] - optional third party receiver address
* @returns {Promise}
*/
const charge = await dapp.charge(amount, target_address);
# amount - number, Payment amount
# target_address - string, optional third party receiver address
def charge
dapp.charge(amount, target_address)
end
# amount - number, Payment amount
# target_address - string, optional third party receiver address
charge = dapp.charge(amount, target_address)
// Param $amount - Payment amount
// Param $target_address - optional third party receiver address
$charge = $dapp->charge($amount, $target_address);
Transfer###
Sends money from the user account to the third party directly.
/**
* @param {number} amount - Payment amount
* @param {string} destination - third party receiver address
* @returns {Promise}
*/
const transfer = await dapp.transfer(amount, destination);
# amount - number, Payment amount
# target_address - string, third party receiver address
def transfer
dapp.transfer(amount, target_address)
end
# amount - number, Payment amount
# target_address - string, third party receiver address
transfer = dapp.transfer(amount, target_address)
// Param $amount - number, Payment amount
// Param $target_address - string, third party receiver address
$transfer = $dapp->transfer($amount, $target_address);
Payout###
Sends money from the DApp account to the user or third party.
/**
* @param {number} amount - Payment amount
* @param {string} [destination] - third party receiver address
* @returns {Promise}
*/
const payout = await dapp.payout(amount, destination);
# amount - number, Payment amount
# target_address - string, third party receiver address
def payout
dapp.payout(amount, target_address)
end
# amount - number, Payment amount
# target_address - string, third party receiver address
payout = dapp.payout(amount, target_address)
// Param $amount - number, Payment amount
// Param $target_address - string, third party receiver address
$payout = $dapp->payout($amount, $target_address);
Example##
The below example shows creating a Charge endpoint that would charge the user the requested amount and optionally transfer it from the DApp account to a third party in the same transaction.
//Using Express
app.post("/charge", async (req, res, next) => {
try {
const APP_SECRET_SEED = 'SD3XEDAPTGELU2L6XMX....KZEZRGJSPQNTXZRIC';
const dapp = await Mobius.AppBuilder.build(APP_SECRET_SEED, req.user.sub);
const { amount, target_address } = req.body;
if (amount === null || isNaN(Number(amount))) {
return res.status(400).json({ error: "Invalid amount" })
}
const response = await dapp.charge(amount, target_address);
res.json({
status: "ok",
tx_hash: response.hash,
balance: dapp.userBalance
});
} catch (e) {
next(e);
}
});
class AppController < ApplicationController
skip_before_action :verify_authenticity_token, :only => [:pay]
ROUND_PRICE = 5
# POST /charge
def charge
dapp.charge(ROUND_PRICE)
rescue Mobius::Client::Error::InsufficientFunds
render :gone
end
private
def token_s
session[:token] = params[:token] || session[:token]
end
def token
@token ||= Mobius::Client::Auth::Jwt.new(Rails.application.secrets.app[:jwt_secret]).decode!(token_s)
rescue Mobius::Client::Error
nil # We treat all invalid tokens as missing
end
def dapp
@dapp ||= token && Mobius::Client::App.new(
# SA2VTRSZPZ5FIC.....I4QD7LBWUUIK
Rails.application.secrets.app[:secret_seed],
token.public_key # Current user public key
)
end
end
@app.route('/api/charge', methods=['POST'])
@login_required
def charge(user_public_key):
try:
dapp = AppBuilder().build(app_secret_seed, user_public_key)
try:
data = json.loads(request.data.decode('utf-8'))
amount = data['amount']
except:
amount = 1
if not amount or not float(amount):
return abort(400, description='Invalid amount')
response = dapp.charge(amount)
hash_meta = binascii.hexlify(response.hash_meta()).decode()
return jsonify({
'status': 'ok',
'tx_hash': hash_meta,
'balance': dapp.user_balance(),
})
except Exception as error:
return error
if($endpoint == '/api/charge' && $_SERVER['REQUEST_METHOD'] == 'POST'){
try{
// decodes JWT and returns the users public key
$public_key = getPublicKey($_GET['token']);
if(isset($_REQUEST['amount'])){
$amount = $_REQUEST['amount'];
}
else{
$amount = 1;
}
$dapp = new Mobius\Client\App(APP_KEY, $public_key);
$response = $dapp->charge($amount);
echo json_encode( array(
'status' => 'ok',
'tx_hash' => $response->getRawData()['hash'],
'balance' => $dapp->balance()
)
);
}catch(\Exception $e){
echo $e->getMessage();
}
die;
}
Using Environment Variables
Make sure you keep all your secret seeds stored safely, extracting into ENV variables or equivalent. Keys present throughout the documentation only serve for example purposes and belong to an account in the Test network.
Updated about 7 years ago