-
Notifications
You must be signed in to change notification settings - Fork 0
Payment API Integration
Our Android libraries let you easily accept mobile payments inside any Android app.
Monri has created a Java library for Android, allowing you to easily submit payments from an Android app. With our mobile library, we address PCI compliance by eliminating the need to send card data directly to your server. Instead, our libraries send the card data directly to our servers, where we can convert them to tokens.
Recently we've added a new way of simplified payment integration in your app.
It consist of two steps:
- create new payment on merchant's backend
- confirm created payment on merchant's mobile application using SDK
New integration is designed to guide customer through payment process in app.
It reduces integration time significantly simply by requiring implementation of two endpoints on merchant's backend.
Continue following this wiki to learn how to create payment on backend.
At some point in the flow of your app, you’ll want to obtain payment details from the user. There are a couple ways to do this:
- Use our built-in card input widget to collect card information
- Build your own credit card form
Instructions for each route follows, although you may want to write your app to offer support for both.
To collect card data from your customers directly, you can use Monri’s CardMultilineWidget in your application. You can include it in any view’s layout file.
<com.monri.android.view.CardMultilineWidget
android:id="@+id/card_input_widget"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
This allows your customers to input all of the required data for their card: the number, the expiration date, and the CVV code. Note that the value of the Card
object is null
if the data in the widget is either incomplete or fails client-side validity checks.
import com.monri.android.view.CardMultilineWidget;
CardMultilineWidget cardInputWidget = (CardMultilineWidget) findViewById(R.id.card_input_widget);
Card cardToSave = cardInputWidget.getCard();
if (cardToSave == null) {
errorDialogHandler.showError("Invalid Card Data");
}
If you have any other data that you would like to associate with the card, such as name, address, or ZIP code, you can put additional input controls on your layout and add them directly to the Card
object.
cardToSave = cardToSave.toBuilder().name("Customer Name").build();
cardToSave = cardToSave.toBuilder().addressZip("12345").build();
If you build your own payment form, you’ll need to collect at least your customers’ card numbers and expiration dates. Monri strongly recommends collecting the CVC. You can optionally collect the user’s name and billing address for additional fraud protection.
Once you’ve collected a customer’s information, you will need to exchange the information for a Monri token.
To create a Card
object from data you’ve collected from other forms, you can create the object with its constructor.
import com.monri.android.model.Card;
//...
//...
public void onAddCard(String cardNumber, String cardExpMonth,
String cardExpYear, String cardCVC) {
final Card card = Card.create(
cardNumber,
cardExpMonth,
cardExpYear,
cardCVC
);
card.validateNumber();
card.validateCVC();
}
As you can see in the example above, the Card
instance contains some helpers to validate that the card number passes the Luhn check, that the expiration date is the future, and that the CVC looks valid. You’ll probably want to validate these three things at once, so we’ve included a validateCard
function that does so.
// The Card class will normalize the card number
final Card card = Card.create("4242-4242-4242-4242", 12, 2020, "123");
if (!card.validateCard()) {
// Show errors
}
This step is preferably executed when you have enough information to create customer's order.
For simplicity we'll show example using curl in PHP.
To create payment on our backend you'll need:
-
merchant_key
(available on merchant's dashboard) -
authenticity_token
(available on merchant's dashboard)
Additionally we require following fields:
field | length | type | description |
---|---|---|---|
amount | 1-11 | Integer | amount is in minor units, ie. 10.24 USD is sent as 1024 |
order_number | 2-40 | String | unique order identifier |
currency | 3 | String | One of supported currencies (BAM, HRK, EUR, USD, CHF etc) |
transaction_type | enum | String | possible values are: authorize or purchase
|
order_info | 3-100 | String | short description of order being processed |
Optionally we offer setting payment scenario, which can be one of:
field | length | type | description |
---|---|---|---|
scenario | enum | String | possible values are: charge or add_payment_method
|
Scenario charge
charges customer amount. Depending on transaction_type
amount is reserved (authorize) or captured (purchase).
Scenario add_payment_method
provides simple way to implement 'Save card for future payments' functionality.
For request authentication we use Authorization
header created from:
- authorization schema: String =
WP3-v2
- authenticity_token: String = value from merchant's configuration
- timestamp: Integer = unix timestamp (eg PHP's time())
- digest: String = sha512(merchant_key + timestamp + authenticity_token + body_as_string)
Parts above are joined by space, so Authorization
header should be in this form:
Authorization: schema authenticity_token timestamp digest
Example: Authorization: WP3-v2 abc...def 1585229134 314d32d1...0b49
Request endpoint is <base_url>/v2/payment/new
where base_url is:
-
https://ipgtest.monri.com
for TEST environment -
https://ipg.monri.com
for PROD environment
TIP: Parametrize merchant_key, authenticity_token and base_url so it can be easily changed when you are ready for production environment.
Payment/new response contains:
- status: String: approved | declined
- id: String - Unique payment identifier used to track payment flow on Monri's side. Useful for debugging if something goes wrong. Save this value in your database.
- client_secret: String - Value you'll send to your application which then will use this secret to confirm payment using Monri Android SDK.
Request example in PHP:
$data = [
"amount" => 100,
// unique order identifier
"order_number" => 'random'. time(),
"currency" => "EUR",
"transaction_type" => "purchase",
"order_info" => "Create payment session order info",
"scenario" => 'charge'
];
$body_as_string = Json::encode($data); // use php's standard library equivalent if Json::encode is not available in your code
$base_url = 'https://ipgtest.monri.com'; // parametrize this value
$ch = curl_init($base_url . '/v2/payment/new');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $body_as_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$timestamp = time();
$digest = hash('sha512', $key . $timestamp .$authenticity_token. $body_as_string);
$authorization = "WP3-v2 $authenticity_token $timestamp $digest";
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($body_as_string),
'Authorization: '.$authorization
)
);
$result = curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
$response = ['client_secret' => null, 'status' => 'declined', 'error' => curl_error($ch)];
} else {
curl_close($ch);
$response = ['status' => 'approved', 'client_secret' => Json::decode($result)['client_secret']];
}
var_dump($response);
After you've created payment on a backend and sent client_secret back to your application you need to confirm payment using Monri's SDK.
Steps:
- ensure you have valid client_secret (created on backend using payment/new)
- create
Monri
instance (inside theonCreate()
method of the activity) -this.monri = new Monri((ActivityResultCaller) this, monriApiOptions);
- collect Customer params and create
CustomerParams
instance - create
TransactionParams
fromcustomerParams
, set other values if needed (eg overrideorder_info
) - obtain
PaymentMethodsParams
:- For card payments: from
Card
instance viacard.toPaymentMethodParams()
- For crypto payments: from
DirectPayment
instance viadirectPayment.toPaymentMethodParams();
- For card payments: from
- create
ConfirmPaymentParams
from client_secret, payment method params and transaction params - create
ActionResultConsumer<PaymentResult>
instance - invoke
monri.confirmPayment(confirmPaymentParams, actionResultConsumer)
- payment result will be returned to the actionResultConsumer instance
NOTE Values set in TransactionParams
and CustomerParams
will override those set in payment/new
request.
CustomerParams:
attribute | length | type | description |
---|---|---|---|
full_name | 3-30 | String | buyer's full name |
address | 3-100 | String | buyer's address |
city | 3-30 | String | buyer's city |
zip | 3-9 | String | buyer's zip |
country | 2 | String | buyer's country - ISO two letter code |
phone | 3-30 | String | buyer's phone |
3-100 | String | buyer's email |
TransactionParams is used to override values set in payment/new
.
Integration example is available in SDK's example and on this link
Although you can easily collect payment result directly in application via onSuccess
call it's better to implement callback listener (WebHook) on your backend.
Requirements:
- it must be available over the Internet
- it must be secured (HTTPS)
- it must be set in merchant's setup on Monri's dashboard (if you are not able to set this value contact [email protected])
How it works:
- upon transaction processing and approval we'll send POST request to callback endpoint defined in merchant's settings
- validate received request to check if it's from us
- update/deliver order
Example of POST request sent to callback endpoint:
Body:
{
"id": 214,
"acquirer": "integration_acq",
"order_number": "3159daf002e3809",
"amount": 100,
"currency": "HRK",
"ch_full_name": "John Doe",
"outgoing_amount": 100,
"outgoing_currency": "HRK",
"approval_code": "687042",
"response_code": "0000",
"response_message": "approved",
"reference_number": "000003036888",
"systan": "000214",
"eci": "06",
"xid": null,
"acsv": null,
"cc_type": "visa",
"status": "approved",
"created_at": "2020-03-26T11:09:17.959+01:00",
"transaction_type": "purchase",
"enrollment": "N",
"authentication": null,
"pan_token": null,
"masked_pan": "411111-xxx-xxx-1111",
"issuer": "xml-sim",
"number_of_installments": null,
"custom_params": "{a:b, c:d}"
}
Headers:
header | value |
---|---|
accept-encoding | gzip;q=1.0,deflate;q=0.6,identity;q=0.3 |
authorization | WP3-callback d5e4528ad8a0e0f4262e518c663d5ff83cd4a8f381db68f9d30f99961409ceebb719c16d423757fc36c532b902c987012f5825dc8d32dde3a9b7ed95876be77a |
content-type | application/json |
http_authorization | WP3-callback d5e4528ad8a0e0f4262e518c663d5ff83cd4a8f381db68f9d30f99961409ceebb719c16d423757fc36c532b902c987012f5825dc8d32dde3a9b7ed95876be77a |
user-agent | Faraday v0.15.4 |
content-length | 621 |
connection | keep-alive |
Where authorization
and http_authorization
headers are created as:
digest = sha512(merchant_key + body)
authorization_header_value = WP3-callback digest
To check if request is valid check:
- if authorization header schema is
WP3-callback
- extract digest as second part
- verify digest