1Pay

SMSplus Charging


Các bước kết nối Smsplus

1 End User gửi "tin nhắn đến" hệ thống 1pay

2 1pay chuyển tin nhắn đến cho Merchant

3 Merchant xử lý và trả "tin nhắn trả về" cho 1pay

4 1pay trả "tin nhắn trả về" cho End User

Mô tả chi tiết

Merchant webservice: là hệ thống merchant xây dựng để nhận request(MO) từ 1Pay gửi sang.

Max request time out:

   -Mobifone: 17 giây.

   -VinaPhone: 8 giây.

   -Viettel: 5 giây/request (2 request).

I. Kiểm tra cú pháp MO.

Request do hệ thống 1Pay gửi sang sẽ ở dạng HTTP GET, với các tham số như sau:

Tham số Mô tả
access_key Đại diện cho sản phẩm của merchant khai báo trong hệ thống 1pay.vn
amount Số tiền trừ vào tài khoản khách hàng. Giá cước (1000,2000,3000,4000,5000,10000,20000,30000,50000,100000)
command_code Là mã dịch vụ của khách hàng. Đăng ký và khai báo trên hệ thống 1Pay. Ví dụ GAME1
mo_message Nội dung tin nhắn của khách hàng ở dạng String bắt đầu từ mã đăng ký.(gamecode napX thong_tin_khac)
Ví dụ gamecode là TEST:
-Tin nhắn lên(mo): MW TEST NAP1 dunglp => mo_message: TEST NAP1 dunglp
msisdn Số điện thoại nhắn tin của khách hàng, theo chuẩn international, bắt đầu bằng 84
telco Mã nhà mạng. Ví dụ: vnp(Vinaphone),vms(Mobifone),vtm(Viettel)
signature Chữ ký, merchant có thể sử dụng signature để kiểm soát an ninh.
access_key=$access_key&amount=$amount&command_code=$command_code&mo_message=$mo_message&msisdn=$msisdn&telco=$telco được hmac bằng thuật toán SHA256

Lưu ý:

[thong_tin_khac] trong nội dung MO chỉ được sử dụng kí tự a tới z, 0 tới 9, '/', '.', '-' để phân biệt các trường nếu muốn, không được có dấu cách hoặc các kí tự đặc biệt khác

Response

Sau khi xử lý, webservice cần trả về nội dung ở dạng json với cấu trúc như sau:

{"status":status, "sms":"nội dung tin nhắn cần trả về cho khách hàng", "type":"loại tin nhắn"}
				

Trong đó

Tham số Mô tả
status Trạng thái xử lý, nhận giá trị:1 - Tin nhắn đúng, 0 - Tin nhắn sai (sai mệnh giá hoặc sai nội dung)
sms Nội dung tin nhắn cần trả về cho khách hàng
type Loại tin nhắn, nhận giá trị: text, hoặc wap_push

Chú ý:

  +Bước 1 hiện tại chỉ hỗ trợ mạng viettel.

  +Khi trả về status là 1 thì khi đó 1pay mới gọi đến bước 2 của merchant.

  +Bước 1 là bước check Mo, merchant ko cần cộng tiền/item cho user.

II. Nhận request.

Request do hệ thống 1Pay gửi sang sẽ ở dạng HTTP GET, với các tham số như sau:

Tham số Mô tả
access_key Đại diện cho sản phẩm của merchant khai báo trong hệ thống 1pay.vn
amount Số tiền trừ vào tài khoản khách hàng. Giá cước (1000,2000,3000,4000,5000,10000,20000,30000,50000,100000)
command_code Là mã dịch vụ của khách hàng. Đăng ký và khai báo trên hệ thống 1Pay ví dụ GAME1
error_code Là mã lỗi 1pay trả về cho khách hàng. Ví dụ (WCG-0000 :Giao dịch thành công) (WCG-0001:Thuê bao không hợp lệ) (WCG-0002: Dữ liệu CP gửi lên sai) ( WCG-0005 Tài khoản không đủ tiền)
error_message Thông báo lỗi 1Pay gửi về cho merchant
mo_message Nội dung tin nhắn của khách hàng ở dạng String bắt đầu từ mã đăng ký.(Mw gamecode napX thong_tin_khac)
Ví dụ gamecode là TEST:
- MW TEST NAP1 dunglp => mo_message: TEST NAP1 dunglp
msisdn Số điện thoại nhắn tin của khách hàng, theo chuẩn international, bắt đầu bằng 84
request_id Id của tin nhắn ở dạng String
request_time Thời gian đầu số nhận được tin nhắn, ở dạng iso, ví dụ: 2013-07-06T22:54:50Z
signature Chữ ký, merchant có thể sử dụng signature để kiểm soát an ninh.
access_key=$access_key&amount=$amount&command_code=$command_code&error_code=$error_code&error_message=$error_message&mo_message=$mo_message&msisdn=$msisdn&request_id=$request_id&request_time=$request_time được hmac bằng thuật toán SHA256

Lưu ý:

[thong_tin_khac] trong nội dung MO chỉ được sử dụng kí tự a tới z, 0 tới 9, '/', '.', '-' để phân biệt các trường nếu muốn, không được có dấu cách hoặc các kí tự đặc biệt khác

Response

Sau khi xử lý, webservice cần trả về nội dung ở dạng json với cấu trúc như sau:

{"status":status, "sms":"nội dung tin nhắn cần trả về cho khách hàng", "type":"loại tin nhắn"}
				

Trong đó

Tham số Mô tả
status Trạng thái xử lý, nhận giá trị: 1- tính phí, user được cộng dịch vụ trong app/website, 0 – không bị tính phí, user không sử dụng được dịch vụ trong app/website.
Các tin nhắn trả sai trạng thái tính cước. Ví dụ: “Giao dich khong thanh cong”, “Khong tim thay tai khoan”, “He thong dang bao tri”, … mà trả status 1 là tin trả sai trạng thái tính cước, 1Pay sẽ tự động chuyển thành status 0 và trả kết quả cho nhà mạng, Merchant không được tính doanh thu những tin này
sms Nội dung tin nhắn cần trả về cho khách hàng
type Loại tin nhắn, nhận giá trị: text, hoặc wap_push

Trong quá trình vận hành, có thể có những tính huống mà Hệ thống SMS của 1Pay sẽ gọi lại những request có cùng request_id. Do đó, hệ thống của đối tác cần đảm bảo nếu đã xử lý một request tương ứng với request_id A, thì những requests tiếp theo có cùng request_id là A sẽ không được xử lý.

III.Truy vấn lại kiểm tra kết quả giao dịch (Query Api)

Địa chỉ nhận Request Charging:

GET  http://merchant.1pay.vn/charging/service/logs

Giao thức truyền dữ liệu: HTTP
Max request time out: 20 giây

Tham số Mô tả
access_key Đại diện cho sản phẩm của merchant khai báo trong hệ thống 1pay.vn
request_id requestId của giao dịch muốn kiểm tra nhận được ở bước 2
charging_type Loại hình dịch vụ (Ở đây là “iac”)
signature Chữ ký, là một chuỗi string:
access_key=$access_key&charging_type=$charging_type&request_id=$request_id
được hmac bằng thuật toán SHA256

Response trả về có dạng json

{"message":"Thông báo từ 1pay ","status":status,"iac":{"amount":":[Mệnh giá tin nhắn],"request_id":"RequestId của giao dịch","status":"Trạng thái phí: 1 - Tính phí, 0 - Không tính phí","msisdn":"Số điện thoại","mo_message":"Nội dung tin nhắn của khách hàng ","billing_status":"Trạng thái thanh toán","mt_message":"Tin nhắn trả về cho khách hàng ","request_time":"Thời gian đầu số nhận được tin nhắn"},"type":"text"}

Trong đó

Tham số Mô tả
status Trạng thái xử lý, nhận giá trị: 0 - Giao dịch thất bại, 1 - Giao dịch thành công
billing_status 0 - Giao dịch chưa bị trừ tiền, 1 - Giao dịch đã trừ tiền
Lên đầu trang

   public static String getCarrierCode(Context context) {
	TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    String mccmnc = telephonyManager.getSimOperator();
    if (mccmnc == null) {
        return null;
    }
    String code = null;
    if (mccmnc.equals("45204")) {
        code = "viettel";
    } else if (mccmnc.equals("45201")) {
        code = "mobifone";
    } else if (mccmnc.equals("45202")) {
        code = "vinaphone";
    } else if (mccmnc.equals("45205")) {
        code = "vietnamobile";
    } else if (mccmnc.equals("45203")) {
        code = "sfone";
    }
    return code;
}

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
(void) OK_getcode_btnTapped:(UIGestureRecognizer*)recognizer
{
    [popup removeFromSuperview];
    //Country : 452 - mobifone: 01, vinaphone: 02, viettel: 04
    CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init] ;
    CTCarrier *carrier = [networkInfo subscriberCellularProvider];
    NSLog(@"Carrier Name: %@ ", [carrier carrierName]);
    NSLog(@"Mobile Country Code: %@", carrier.mobileCountryCode);
    NSLog(@"Mobile Network Code: %@", carrier.mobileNetworkCode);
    if ([carrier.mobileCountryCode isEqualToString:@"452"]&&[carrier.mobileNetworkCode isEqualToString:@"01"]){
        [self send_sms:1];
    } else if ([carrier.mobileCountryCode isEqualToString:@"452"]&&[carrier.mobileNetworkCode isEqualToString:@"02"]) {
        [self send_sms:1];
    } else if ([carrier.mobileCountryCode isEqualToString:@"452"]&&[carrier.mobileNetworkCode isEqualToString:@"04"]) {
        [self send_sms:2];
    } else {
        NSLog(@"Can not call send sms method");
    }
}
package service;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.json.JSONException;
import org.json.JSONObject;

@Path("/")
public class SMSPlus {
	
	@GET
	@Path("/SmsplusCharging")
	@Produces(MediaType.APPLICATION_JSON)
	public Response charging(
			@DefaultValue("0")@QueryParam("access_key")String accKey,
			@DefaultValue("0")@QueryParam("amount")String amount,
			@DefaultValue("0")@QueryParam("command_code")String commandCode,
			@DefaultValue("0")@QueryParam("error_code")String errorCode,
			@DefaultValue("0")@QueryParam("error_message")String errorMessage,
			@QueryParam("mo_message")String moMessage,
			@QueryParam("msisdn")String phone,
			@QueryParam("request_id")String requestId,
			@QueryParam("request_time")String requestTime,
			@QueryParam("signature")String signature){
		
		JSONObject json = new JSONObject();
		String secret = ""; // Secret Key do 1pay cung cap. thay bang Secret Key cua ban
		String sig = generateSignature(accKey, amount, commandCode, errorCode, errorMessage, moMessage, phone, requestId, requestTime, secret);
		try {
			if (signature.equalsIgnoreCase(sig)) {
				json.put("status", 1);
				json.put("sms", "Send sms thanh cong");
			}else {
				json.put("status", 0);
				json.put("sms", "tin nhan sai cu phap");
			}
			json.put("type", "text");
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return Response.status(200).entity(json.toString()).build();
	}
	
	public static String hmac(String msg, String keyString, String algo) {
		String digest = "";
		try {
			if (keyString != null && keyString.length() > 0) {
				SecretKeySpec keySpec = new SecretKeySpec(
						keyString.getBytes("UTF-8"), algo);
				Mac mac = Mac.getInstance(algo);
				mac.init(keySpec);
				byte[] bytes = mac.doFinal(msg.getBytes("ASCII"));
				StringBuffer hash = new StringBuffer();
				for (int i = 0; i < bytes.length; i++) {
					String hex = Integer.toHexString(0xFF & bytes[i]);
					if (hex.length() == 1) {
						hash.append('0');
					}
					hash.append(hex);
				}
				digest = hash.toString();

			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return digest;
	}

	public String generateSignature(String access_key, String amount,
			String command_code, String error_code, String error_message,
			String mo_message, String phone, String request_id,
			String request_time, String secret) {
		String urlParameters = "";
		String signature = "";
		if (access_key != null && amount != null && command_code != null
				&& error_code != null && error_message != null
				&& mo_message != null && phone != null && request_id != null
				&& request_time != null && secret != null) {
			urlParameters = "access_key=%access_key%&amount=%amount%&command_code=%command_code%"
					+ "&error_code=%error_code%&error_message=%error_message%&mo_message=%mo_message%"
					+ "&msisdn=%msisdn%&request_id=%request_id%&request_time=%request_time%";
			urlParameters = urlParameters.replaceFirst("%access_key%",access_key);
			urlParameters = urlParameters.replaceFirst("%amount%", amount);
			urlParameters = urlParameters.replaceFirst("%command_code%",command_code);
			urlParameters = urlParameters.replaceFirst("%error_code%",error_code);
			urlParameters = urlParameters.replaceFirst("%error_message%",error_message);
			urlParameters = urlParameters.replaceFirst("%mo_message%",mo_message);
			urlParameters = urlParameters.replaceFirst("%msisdn%", phone);
			urlParameters = urlParameters.replaceFirst("%request_id%",request_id);
			urlParameters = urlParameters.replaceFirst("%request_time%",request_time);
			signature = hmac(urlParameters, secret, "HmacSHA256");
			System.out.println("Signature:" + signature);
		}
		return signature;
	}
}
                                    

  File check_mo_smsplus.php

    $arParams['access_key'] = $_GET['access_key'] ? $_GET['access_key'] : '';
   $arParams['amount'] = $_GET['amount'] ? $_GET['amount'] : '';
   $arParams['command_code'] = $_GET['command_code'] ? $_GET['command_code'] : '';
   $arParams['mo_message'] = $_GET['mo_message'] ? $_GET['mo_message'] : '';
   $arParams['msisdn'] = $_GET['msisdn'] ? $_GET['msisdn'] : '';
   $arParams['telco'] = $_GET['telco'] ? $_GET['telco'] : '';
   $arParams['signature'] = $_GET['signature'] ? $_GET['signature'] : '';
   $data = "access_key=" . $arParams['access_key'] . "&amount=" . $arParams['amount'] . "&command_code=" . $arParams['command_code'] . "&mo_message=" . $arParams['mo_message'] . "&msisdn=" . $arParams['msisdn'] . "&telco=" . $arParams['telco'];
   $secret = ''; //product's secret key (get value from 1Pay product detail)
   $signature = hash_hmac("sha256", $data, $secret); // create signature to check
   $arResponse['type'] = 'text';
   // kiem tra signature neu can
   if ($arParams['signature'] == $signature) {
    //if sms content and amount and ... are ok. return success case
    $arResponse['status'] = 1;
    $arResponse['sms'] = 'Hop le';
   }
   else {
    //if not. return fail case
	$arResponse['status'] = 0;
    $arResponse['sms'] = 'Khong hop le';
   }
   // return json for 1pay system
   echo json_encode($arResponse);

  File smsplus1pay.php

    $arParams['access_key'] = $_GET['access_key'] ? $_GET['access_key'] : '';
   $arParams['command_code'] = $_GET['command_code'] ? $_GET['command_code'] : '';
   $arParams['mo_message'] = $_GET['mo_message'] ? $_GET['mo_message'] : '';
   $arParams['msisdn'] = $_GET['msisdn'] ? $_GET['msisdn'] : '';
   $arParams['request_id'] = $_GET['request_id'] ? $_GET['request_id'] : '';
   $arParams['request_time'] = $_GET['request_time'] ? $_GET['request_time'] : '';
   $arParams['amount'] = $_GET['amount'] ? $_GET['amount'] : '';
   $arParams['signature'] = $_GET['signature'] ? $_GET['signature'] : '';
   $arParams['error_code'] = $_GET['error_code'] ? $_GET['error_code'] : '';
   $arParams['error_message'] = $_GET['error_message'] ? $_GET['error_message'] : '';
   $data = "access_key=" . $arParams['access_key'] . "&amount=" . $arParams['amount'] . "&command_code=" . $arParams['command_code'] . "&error_code=" . $arParams['error_code'] . "&error_message=" . $arParams['error_message'] . "&mo_message=" . $arParams['mo_message'] . "&msisdn=" . $arParams['msisdn'] . "&request_id=" . $arParams['request_id'] . "&request_time=" . $arParams['request_time'];
   $secret = ''; //product's secret key (get value from 1Pay product detail)
   $signature = hash_hmac("sha256", $data, $secret); // create signature to check
   $arResponse['type'] = 'text';
   // kiem tra signature neu can
   if ($arParams['signature'] == $signature) {
    //if sms content and amount and ... are ok. return success case
    $arResponse['status'] = 1;
    $arResponse['sms'] = 'Giao dich thanh cong ... Lien he ... de biet them chi tiet';
   }
   else {
    //if not. return fail case
	$arResponse['status'] = 0;
    $arResponse['sms'] = 'Giao dich khong thanh cong. Lien he ... de biet them chi tiet.';
   }
   // return json for 1pay system
   echo json_encode($arResponse);
     Download 1pay.dll

   using System.Web;
   using System.Web.Script.Serialization;
   using System.Web.Script.Services;
   using System.Web.Services;
   using _1Pay;

   namespace InAppChargingService
   {
    public class InAppService : System.Web.Services.WebService
    {
     [WebMethod]
     [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
     public void inAppChargingRequest(string access_key, string amount, string command_code, string error_code, string error_message, string mo_message, string msisdn, string request_id, string request_time, string signature)
     {
      string secretKey = ""; //product's secret key (get value from 1Pay product detail)
      My1Pay my1Pay = new My1Pay();
      string signatureTrue = my1Pay.generateSignature_InApp(access_key, amount, command_code, error_code, error_message, mo_message, msisdn, request_id,
                 request_time, secretKey);
      string json = "";
      var serializer = new JavaScriptSerializer();
      //Security check at the merchant, not mandatory
      if (signature == signatureTrue)
      {
       //decode url mo, to do something with mo, return response to 1pay, example:
       json = serializer.Serialize(new { status = 1, sms = "mt success", type = "text" });
      }
      else
      {
       //to do something example:
       json = serializer.Serialize(new { status = 0, sms = "mt fail", type = "text" }); 
      }
      HttpContext.Current.Response.ContentType = "application/json; charset=utf-8";
      HttpContext.Current.Response.Write(json);
     }
    }
   }
     Download hmac-sha256.cpp

   #include <iostream>
   #include <cgicc/Cgidefs.h> 
   #include <cgicc/Cgicc.h>
   #include <cgicc/Httphtmlheader.h>
   #include <cgicc/Htmlclasses.h>  
   #include "hmac-sha256.cpp"
   using namespace std;
   using namespace cgicc;
   int main ()
   {
      Cgicc cgicc;
      cout  "Content-Type: application/json\r\n\r\n"; // set json return
      string access_key = cgicc("access_key");
      string amount =cgicc("amount"); 
      string command_code = cgicc("command_code");
      string error_code = cgicc("error_code");
      string error_message = cgicc("error_message");
      string mo_messae = cgicc("mo_message");
      string msisdn = cgicc("msisdn");
      string request_id = cgicc("request_id");
      string request_time = cgicc("request_time");
      string signature = cgicc("signature");
      string secret_key=""; //product's secret key (get value from 1Pay product detail)
      string data = "access_key=" +access_key+ "&amount=" +amount+ "&command_code=" +command_code+ "&error_code=" +error_code+ "&error_message=" +error_message+ "&mo_message=" +mo_message+ "&msisdn=" +msisdn+ "&request_id=" +request_id+ "&request_time=" +request_time;
      string sigGen = hmac(secret_key,data); // create signatue
	  //processing data and return result for 1pay
      if (sigGen.compare(signature)==0){
	   //if all sms content, amount, ... are ok. return success case
       cout <<  "{\"status\":1,\"type\":\"text\",\"sms\":\"Giao dich thanh cong ... Lien he ... de biet them chi tiet\"}" ;
       }
      else{
	   //if not. return fail case
       cout <<  "{\"status\":0,\"type\":\"text\",\"sms\":\"Giao dich khong thanh cong. Lien he ... de biet them chi tiet.\"}" ;
      }  
      return 0;
   }