Skip to content

Instantly share code, notes, and snippets.

@rizdaprasetya
Last active November 5, 2019 05:11
Show Gist options
  • Save rizdaprasetya/9d16893578d600a03075939ef74c5c1f to your computer and use it in GitHub Desktop.
Save rizdaprasetya/9d16893578d600a03075939ef74c5c1f to your computer and use it in GitHub Desktop.
Midtrans 3ds new flow
<?php
$client_key = 'SB-Mid-client-61XuGAwQ8Bj8LxSS';
$server_key = 'SB-Mid-server-GwUP_WGbJPXsDzsNEBRs8IYA';
// This part is handling API `/charge` process triggered from AJAX call by frontend (POST request), return RAW API response
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
$ch = curl_init();
$req_body = "{ \"payment_type\": \"credit_card\", \"transaction_details\": { \"order_id\": \"php-test-".time()."\", \"gross_amount\": 100000 }, \"credit_card\": { \"token_id\": \"".$json_obj->token_id."\", \"authentication\": ".($json_obj->authenticate_3ds?"\"true\"":"\"false\"")." }}";
curl_setopt($ch, CURLOPT_URL, 'https://api.sandbox.midtrans.com/v2/charge');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req_body);
curl_setopt($ch, CURLOPT_POST, 1);
$headers = array();
$headers[] = 'Accept: application/json';
$headers[] = 'Authorization: Basic '.base64_encode($server_key.':');
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close ($ch);
echo $result;
exit();
}
// This part is the frontend, showing HTML+JS page (on GET request)
?>
<html>
<head>
<title>Core API Credit Card Frontend Sample</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism.css" />
<style type="text/css">
pre{
font-size:75%;
}
/* Fix for fieldset width overflowing parent width */
fieldset {
min-width: 0;
max-width: 100%;
}
</style>
</head>
<body>
<!-- TODO change data-environment to `production` for Production mode -->
<!-- TODO change data-client-key to your production client key for Production mode -->
<script id= "midtrans-script" src="https://api.midtrans.com/v2/assets/js/midtrans-new-3ds.min.js" data-environment="sandbox" data-client-key="<?php echo $client_key ?>" type="text/javascript"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/picomodal/3.0.0/picoModal.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.js"></script>
<p>This page is to further demonstrate how to implement frontend processes for Credit Card transactions</p>
<form id="token-form">
<fieldset>
<legend>1. Get Card Token Step</legend>
<p>
<label>Card Number</label>
<input id="card-number" value="4811 1111 1111 1114" size="23" type="text" autocomplete="off" />
</p>
<p>
<label>Expiration (MM/YYYY)</label>
<input id="card-expiry-month" value="12" placeholder="MM" size="2" type="text" />
<span> / </span>
<input id="card-expiry-year" value="2020" placeholder="YYYY" size="4" type="text" />
</p>
<p>
<label>CVV</label>
<input id="card-cvv" value="123" size="4" type="password" autocomplete="off" />
</p>
<button class="submit-button" type="submit">Process</button>
<p>
<label>Result card token_id</label>
<input type="text" id="token_id_result">
</p>
<p>
<label>JS implementation:</label>
<pre><code class="language-html" id="token-implementation-lib"></code></pre>
<pre><code class="language-javascript" id="token-implementation"></code></pre>
</p>
</fieldset>
</form>
<br>
<form id="charge-form">
<fieldset>
<legend>2. Charge process is done via Backend</legend>
<p>
<label>Card Token ID</label>
<input type="text" id="token_id" placeholder="481111-1114-0b9ddf4d-eb29-4d29-8750-15555531d2e7" required>
</p>
<p>
<label>3DS Authentication enabled</label>
<input type="checkbox" id="authenticate_3ds" checked>
</p>
<button class="submit-button" type="submit">Process</button>
<p>
<label>Result redirect url (if 3DS)</label>
<input type="text" id="redirect_url_result">
</p>
<small>Check <a href="https://github.com/Midtrans/midtrans-nodejs-client/blob/fc7483503452c41aad8a69df28ebe57aef498505/examples/expressApp/app.js#L67-L77">this code</a>, section `Using Core API - Credit Card` for the backend implementation</small>
<pre><code class="language-javascript">// sample charge in CURL
curl -X POST \
https://api.sandbox.midtrans.com/v2/charge \
-H 'Accept: application/json' \
-H 'Authorization: Basic < From your server_key >' \
-H 'Content-Type: application/json' \
-d '{
"payment_type": "credit_card",
"transaction_details": {
"order_id": "order102-2-1562650595",
"gross_amount": 100000
},
"credit_card": {
"token_id": "< Token ID from Get Token Step >",
"authentication": true,
"installment_term": 12, // optional for installment
"bank": "bni" // optional for installment
}
}'
</code></pre>
</fieldset>
</form>
<br>
<form id="authentication-form">
<fieldset>
<legend>3. 3DS Authentication Step</legend>
<p>
<label>Redirect URL</label>
<input id="redirect_url" placeholder="https://api.sandbox.veritrans.co.id/v2/token/rba/redirect/481111-1114-004cebd3-8fb0-48f7-bf49-eb4e5df54602" size="35" type="text" required/>
</p>
<button class="submit-button" type="submit">Process</button>
<p>
<label>Result:</label>
<pre><code id="result" class="language-javascript">...</code></pre>
</p>
<p>
<label>JS implementation:</label>
<pre><code class="language-javascript" id="authentication-implementation"></code></pre>
</p>
</fieldset>
</form>
<!-- JS implementation to get card token -->
<script type="text/javascript" id="js-card-token">
document.querySelector("#token-form").onsubmit = function(e){
var card = {
"card_number": document.querySelector("#card-number").value,
"card_exp_month": document.querySelector("#card-expiry-month").value,
"card_exp_year": document.querySelector("#card-expiry-year").value,
"card_cvv": document.querySelector("#card-cvv").value,
};
MidtransNew3ds.getCardToken(
card,
{
onSuccess: function(response){
// Success to get card token_id, implement as you wish here:
document.querySelector("#token_id_result").value = response.token_id;
document.querySelector("#token_id").value = response.token_id;
},
onFailure: function(response){
// Fail to get card token_id
alert(`Fail to get card token_id: ${JSON.stringify(response)}`);
console.log(`Fail to get card token_id: ${JSON.stringify(response)}`);
}
}
);
return e.preventDefault() && 0; // prevent form submit
}
</script>
<!-- JS implementation to handle 3DS authentication popup -->
<script type="text/javascript" id="js-authentication">
document.querySelector("#authentication-form").onsubmit = function(e){
var redirect_url = document.querySelector("#redirect_url").value;
// Utilize MidtransNew3Ds.authenticate function to perform 3DS authentication,
// and handle the success/failure result
MidtransNew3ds.authenticate(
redirect_url,
{
performAuthentication: function(redirect_url){
// open iframe to display 3ds authentication redirect_url to customer
popupModal.openPopup(redirect_url);
},
onSuccess: function(response){
// 3ds authentication success, implement payment success scenario
popupModal.closePopup();
document.querySelector("#result").innerText = JSON.stringify(response,null,2);
},
onFailure: function(response){
// 3ds authentication failure, implement payment failure scenario
popupModal.closePopup();
document.querySelector("#result").innerText = JSON.stringify(response,null,2);
},
onPending: function(response){
// transaction is pending, transaction result will be notified later via POST notification
popupModal.closePopup();
document.querySelector("#result").innerText = JSON.stringify(response,null,2)
}
}
);
return e.preventDefault() && 0; // prevent form submit
}
/**
* Alternatively instead of opening 3ds authentication redirect_url using iframe,
* you can also redirect customer using:
```
MidtransNew3ds.redirect(redirect_url, {
callbackUrl : 'https://mywebsite.com/finish_3ds'
});
```
*
*/
/**
* helper functions to open Iframe popup, you may replace this with your own method to open iframe
* PicoModal library is used:
* <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/picomodal/3.0.0/picoModal.js"><\/script>
*/
var popupModal = (function(){
var modal = null;
return {
openPopup(url){
modal = picoModal({
content:'<iframe frameborder="0" style="height:90vh; width:100%;" src="'+url+'"></iframe>',
width: "75%",
closeButton: false,
overlayClose: false,
escCloses: false
}).show();
},
closePopup(){
try{
modal.close();
} catch(e) {}
}
}
}());
</script>
<!-- Unrelated script, for demo purpose -->
<script type="text/javascript">
document.querySelector("#token-implementation-lib").innerHTML = '&lt;!-- Include Midtrans JS library --&gt; \n'+ document.querySelector("#midtrans-script")
.outerHTML
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\\/g, "");
document.querySelector("#token-implementation").innerHTML = document.querySelector("#js-card-token")
.innerHTML
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\\/g, "");
document.querySelector("#authentication-implementation").innerHTML = document.querySelector("#js-authentication")
.innerHTML
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\\/g, "");
document.querySelector("#charge-form").onsubmit = function(e){
popupModal.openPopup('data:text/html,requesting...');
fetch("index.php", {
method : "POST",
body: JSON.stringify({
"token_id" : document.querySelector("#token_id").value,
"authenticate_3ds" : document.querySelector('#authenticate_3ds').checked
}),
headers: {'Content-Type': 'application/json'},
})
.then(function(response) { return response.json(); })
.then(function(responseObj){
if(responseObj.redirect_url){
document.querySelector("#redirect_url").value = responseObj.redirect_url;
document.querySelector("#redirect_url_result").value = responseObj.redirect_url;
} else if (responseObj.status_code){
document.querySelector("#result").innerText = JSON.stringify(responseObj,null,2);
popupModal.closePopup();
document.querySelector("#result").scrollIntoView();
}
})
.catch(function(err){
alert(`Fail to get charge response: ${JSON.stringify(err)}`);
console.log(`Fail to get charge response: ${JSON.stringify(err)}`);
})
.finally(function(){
popupModal.closePopup();
})
return e.preventDefault() && 0; // prevent form submit
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment