mirror of
https://github.com/Nezumi-2711/Foodify.git
synced 2026-09-22 13:38:41 +00:00
Add Zalo payment method
This commit is contained in:
@@ -51,6 +51,7 @@ dependencies {
|
|||||||
implementation 'com.google.android.gms:play-services-auth:20.4.1'
|
implementation 'com.google.android.gms:play-services-auth:20.4.1'
|
||||||
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
|
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
|
||||||
implementation 'com.google.android.gms:play-services-maps:18.1.0'
|
implementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||||
|
implementation files('libs/zpdk-release-v3.1.aar')
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
@@ -83,4 +84,5 @@ dependencies {
|
|||||||
//Firebase
|
//Firebase
|
||||||
implementation platform('com.google.firebase:firebase-bom:31.2.3')
|
implementation platform('com.google.firebase:firebase-bom:31.2.3')
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
||||||
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -30,7 +30,17 @@
|
|||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activity.OrderCheckOutActivity"
|
android:name=".Activity.OrderCheckOutActivity"
|
||||||
android:exported="false" />
|
android:launchMode="singleTask"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="demozpdk"
|
||||||
|
android:host="app" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activity.FavoriteFoodActivity"
|
android:name=".Activity.FavoriteFoodActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package com.capstone.foodify.Activity;
|
package com.capstone.foodify.Activity;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.StrictMode;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
@@ -17,6 +23,8 @@ import android.widget.Spinner;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.widget.NestedScrollView;
|
import androidx.core.widget.NestedScrollView;
|
||||||
@@ -31,8 +39,15 @@ import com.capstone.foodify.Model.Address;
|
|||||||
import com.capstone.foodify.Model.DistrictWardResponse;
|
import com.capstone.foodify.Model.DistrictWardResponse;
|
||||||
import com.capstone.foodify.Model.GoogleMap.GoogleMapResponse;
|
import com.capstone.foodify.Model.GoogleMap.GoogleMapResponse;
|
||||||
import com.capstone.foodify.R;
|
import com.capstone.foodify.R;
|
||||||
|
import com.capstone.foodify.ZaloPay.Api.CreateOrder;
|
||||||
import com.google.android.gms.maps.model.LatLng;
|
import com.google.android.gms.maps.model.LatLng;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
import com.thecode.aestheticdialogs.AestheticDialog;
|
||||||
|
import com.thecode.aestheticdialogs.DialogStyle;
|
||||||
|
import com.thecode.aestheticdialogs.DialogType;
|
||||||
|
import com.thecode.aestheticdialogs.OnDialogClickListener;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -41,6 +56,10 @@ import java.util.List;
|
|||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
import vn.zalopay.sdk.Environment;
|
||||||
|
import vn.zalopay.sdk.ZaloPayError;
|
||||||
|
import vn.zalopay.sdk.ZaloPaySDK;
|
||||||
|
import vn.zalopay.sdk.listeners.PayOrderListener;
|
||||||
|
|
||||||
public class OrderCheckOutActivity extends AppCompatActivity {
|
public class OrderCheckOutActivity extends AppCompatActivity {
|
||||||
private static final Double LAT_SHOP = 16.0500622;
|
private static final Double LAT_SHOP = 16.0500622;
|
||||||
@@ -54,7 +73,7 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
EditText edt_address;
|
EditText edt_address;
|
||||||
Spinner spn_list_address, spn_district, spn_ward;
|
Spinner spn_list_address, spn_district, spn_ward;
|
||||||
ConstraintLayout manual_input_address_layout;
|
ConstraintLayout manual_input_address_layout;
|
||||||
Button change_address_button, confirm_address_button;
|
Button change_address_button, confirm_address_button, btn_ZaloPay;
|
||||||
RadioButton list_address_input, auto_detect_location, manual_input_address, radio_button_selected, take_food_from_shop;
|
RadioButton list_address_input, auto_detect_location, manual_input_address, radio_button_selected, take_food_from_shop;
|
||||||
RadioGroup address_option;
|
RadioGroup address_option;
|
||||||
ConstraintLayout progress_layout;
|
ConstraintLayout progress_layout;
|
||||||
@@ -97,6 +116,7 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
progress_layout = findViewById(R.id.progress_layout);
|
progress_layout = findViewById(R.id.progress_layout);
|
||||||
txt_distance = findViewById(R.id.txt_distance);
|
txt_distance = findViewById(R.id.txt_distance);
|
||||||
txt_ship_cost = findViewById(R.id.txt_ship_cost);
|
txt_ship_cost = findViewById(R.id.txt_ship_cost);
|
||||||
|
btn_ZaloPay = findViewById(R.id.btnZaloPay);
|
||||||
|
|
||||||
|
|
||||||
if(getIntent() != null){
|
if(getIntent() != null){
|
||||||
@@ -104,6 +124,13 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
txt_total.setText(Common.changeCurrencyUnit(total));
|
txt_total.setText(Common.changeCurrencyUnit(total));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StrictMode.ThreadPolicy policy = new
|
||||||
|
StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||||
|
StrictMode.setThreadPolicy(policy);
|
||||||
|
|
||||||
|
// ZaloPay SDK Init
|
||||||
|
ZaloPaySDK.init(2553, Environment.SANDBOX);
|
||||||
|
|
||||||
//Show list food in basket
|
//Show list food in basket
|
||||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
|
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
|
||||||
recyclerView.setLayoutManager(linearLayoutManager);
|
recyclerView.setLayoutManager(linearLayoutManager);
|
||||||
@@ -170,6 +197,8 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
disableAllInputAddressOption();
|
disableAllInputAddressOption();
|
||||||
|
|
||||||
getDistanceAndCalculateShipCost(finalAddress);
|
getDistanceAndCalculateShipCost(finalAddress);
|
||||||
|
|
||||||
|
progress_layout.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -181,6 +210,92 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
onBackPressed();
|
onBackPressed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initZaloPaymentMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initZaloPaymentMethod(){
|
||||||
|
// handle CreateOrder
|
||||||
|
btn_ZaloPay.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
|
||||||
|
//Check address is null or not
|
||||||
|
if(finalAddress != null){
|
||||||
|
|
||||||
|
CreateOrder orderApi = new CreateOrder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject data = orderApi.createOrder(String.valueOf((int) total));
|
||||||
|
String code = data.getString("return_code");
|
||||||
|
|
||||||
|
|
||||||
|
if (code.equals("1")) {
|
||||||
|
//Success create order
|
||||||
|
String token = data.getString("zp_trans_token");
|
||||||
|
|
||||||
|
ZaloPaySDK.getInstance().payOrder(OrderCheckOutActivity.this, token, "demozpdk://app", new PayOrderListener() {
|
||||||
|
@Override
|
||||||
|
public void onPaymentSucceeded(final String transactionId, final String transToken, final String appTransID) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new AestheticDialog.Builder(OrderCheckOutActivity.this, DialogStyle.FLAT, DialogType.SUCCESS)
|
||||||
|
.setTitle("Thành công!")
|
||||||
|
.setMessage("Bạn đã thanh toán thành công cho đơn hàng #" + transactionId)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setOnClickListener(new OnDialogClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(@NonNull AestheticDialog.Builder builder) {
|
||||||
|
Common.LIST_BASKET_FOOD.clear();
|
||||||
|
startActivity(new Intent(OrderCheckOutActivity.this, MainActivity.class));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPaymentCanceled(String zpTransToken, String appTransID) {
|
||||||
|
new AestheticDialog.Builder(OrderCheckOutActivity.this, DialogStyle.RAINBOW, DialogType.INFO)
|
||||||
|
.setTitle("Thông báo!")
|
||||||
|
.setMessage("Huỷ thanh toán thành công!")
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPaymentError(ZaloPayError zaloPayError, String zpTransToken, String appTransID) {
|
||||||
|
new AestheticDialog.Builder(OrderCheckOutActivity.this, DialogStyle.FLAT, DialogType.ERROR)
|
||||||
|
.setTitle("Thanh toán chưa thành công!")
|
||||||
|
.setMessage("Xin vui lòng thử lại!")
|
||||||
|
.setCancelable(true)
|
||||||
|
.setOnClickListener(new OnDialogClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(@NonNull AestheticDialog.Builder builder) {
|
||||||
|
Common.LIST_BASKET_FOOD.clear();
|
||||||
|
startActivity(new Intent(OrderCheckOutActivity.this, MainActivity.class));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(OrderCheckOutActivity.this, "Bạn chưa chọn địa chỉ!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getDistanceAndCalculateShipCost(String address) {
|
private void getDistanceAndCalculateShipCost(String address) {
|
||||||
@@ -510,4 +625,10 @@ public class OrderCheckOutActivity extends AppCompatActivity {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
ZaloPaySDK.getInstance().onResult(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Api;
|
||||||
|
|
||||||
|
import com.capstone.foodify.ZaloPay.Constant.AppInfo;
|
||||||
|
import com.capstone.foodify.ZaloPay.Helper.Helpers;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import okhttp3.FormBody;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
|
||||||
|
public class CreateOrder {
|
||||||
|
private class CreateOrderData {
|
||||||
|
String AppId;
|
||||||
|
String AppUser;
|
||||||
|
String AppTime;
|
||||||
|
String Amount;
|
||||||
|
String AppTransId;
|
||||||
|
String EmbedData;
|
||||||
|
String Items;
|
||||||
|
String BankCode;
|
||||||
|
String Description;
|
||||||
|
String Mac;
|
||||||
|
|
||||||
|
private CreateOrderData(String amount) throws Exception {
|
||||||
|
long appTime = new Date().getTime();
|
||||||
|
AppId = String.valueOf(AppInfo.APP_ID);
|
||||||
|
AppUser = "Android_Demo";
|
||||||
|
AppTime = String.valueOf(appTime);
|
||||||
|
Amount = amount;
|
||||||
|
AppTransId = Helpers.getAppTransId();
|
||||||
|
EmbedData = "{}";
|
||||||
|
Items = "[]";
|
||||||
|
BankCode = "zalopayapp";
|
||||||
|
Description = "Hoá đơn thanh toán cho đơn hàng #" + Helpers.getAppTransId();
|
||||||
|
String inputHMac = String.format("%s|%s|%s|%s|%s|%s|%s",
|
||||||
|
this.AppId,
|
||||||
|
this.AppTransId,
|
||||||
|
this.AppUser,
|
||||||
|
this.Amount,
|
||||||
|
this.AppTime,
|
||||||
|
this.EmbedData,
|
||||||
|
this.Items);
|
||||||
|
|
||||||
|
Mac = Helpers.getMac(AppInfo.MAC_KEY, inputHMac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject createOrder(String amount) throws Exception {
|
||||||
|
CreateOrderData input = new CreateOrderData(amount);
|
||||||
|
|
||||||
|
RequestBody formBody = new FormBody.Builder()
|
||||||
|
.add("app_id", input.AppId)
|
||||||
|
.add("app_user", input.AppUser)
|
||||||
|
.add("app_time", input.AppTime)
|
||||||
|
.add("amount", input.Amount)
|
||||||
|
.add("app_trans_id", input.AppTransId)
|
||||||
|
.add("embed_data", input.EmbedData)
|
||||||
|
.add("item", input.Items)
|
||||||
|
.add("bank_code", input.BankCode)
|
||||||
|
.add("description", input.Description)
|
||||||
|
.add("mac", input.Mac)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
JSONObject data = HttpProvider.sendPost(AppInfo.URL_CREATE_ORDER, formBody);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Api;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.CipherSuite;
|
||||||
|
import okhttp3.ConnectionSpec;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.TlsVersion;
|
||||||
|
|
||||||
|
public class HttpProvider {
|
||||||
|
public static JSONObject sendPost(String URL, RequestBody formBody) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||||
|
.tlsVersions(TlsVersion.TLS_1_2)
|
||||||
|
.cipherSuites(
|
||||||
|
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectionSpecs(Collections.singletonList(spec))
|
||||||
|
.callTimeout(5000, TimeUnit.MILLISECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(URL)
|
||||||
|
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.post(formBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
|
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
Log.println(Log.ERROR, "BAD_REQUEST", response.body().string());
|
||||||
|
data = null;
|
||||||
|
} else {
|
||||||
|
data = new JSONObject(response.body().string());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Constant;
|
||||||
|
|
||||||
|
public class AppInfo {
|
||||||
|
public static final int APP_ID = 2553;
|
||||||
|
public static final String MAC_KEY = "PcY4iZIKFCIdgZvA6ueMcMHHUbRLYjPL";
|
||||||
|
public static final String URL_CREATE_ORDER = "https://sb-openapi.zalopay.vn/v2/create";
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Helper.HMac;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
public class HMacUtil {
|
||||||
|
public final static String HMACMD5 = "HmacMD5";
|
||||||
|
public final static String HMACSHA1 = "HmacSHA1";
|
||||||
|
public final static String HMACSHA256 = "HmacSHA256";
|
||||||
|
public final static String HMACSHA512 = "HmacSHA512";
|
||||||
|
public final static Charset UTF8CHARSET = StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
public final static LinkedList<String> HMACS = new LinkedList<String>(Arrays.asList("UnSupport", "HmacSHA256", "HmacMD5", "HmacSHA384", "HMacSHA1", "HmacSHA512"));
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
private static byte[] HMacEncode(final String algorithm, final String key, final String data) {
|
||||||
|
Mac macGenerator = null;
|
||||||
|
try {
|
||||||
|
macGenerator = Mac.getInstance(algorithm);
|
||||||
|
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);
|
||||||
|
macGenerator.init(signingKey);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (macGenerator == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] dataByte = null;
|
||||||
|
try {
|
||||||
|
dataByte = data.getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return macGenerator.doFinal(dataByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating a message authentication code (MAC) involving a cryptographic
|
||||||
|
* hash function in combination with a secret cryptographic key.
|
||||||
|
*
|
||||||
|
* The result will be represented base64-encoded string.
|
||||||
|
*
|
||||||
|
* @param algorithm A cryptographic hash function (such as MD5 or SHA-1)
|
||||||
|
*
|
||||||
|
* @param key A secret cryptographic key
|
||||||
|
*
|
||||||
|
* @param data The message to be authenticated
|
||||||
|
*
|
||||||
|
* @return Base64-encoded HMAC String
|
||||||
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public static String HMacBase64Encode(final String algorithm, final String key, final String data) {
|
||||||
|
byte[] hmacEncodeBytes = HMacEncode(algorithm, key, data);
|
||||||
|
if (hmacEncodeBytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Base64.getEncoder().encodeToString(hmacEncodeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating a message authentication code (MAC) involving a cryptographic
|
||||||
|
* hash function in combination with a secret cryptographic key.
|
||||||
|
*
|
||||||
|
* The result will be represented hex string.
|
||||||
|
*
|
||||||
|
* @param algorithm A cryptographic hash function (such as MD5 or SHA-1)
|
||||||
|
*
|
||||||
|
* @param key A secret cryptographic key
|
||||||
|
*
|
||||||
|
* @param data The message to be authenticated
|
||||||
|
*
|
||||||
|
* @return Hex HMAC String
|
||||||
|
*/
|
||||||
|
public static String HMacHexStringEncode(final String algorithm, final String key, final String data) {
|
||||||
|
byte[] hmacEncodeBytes = HMacEncode(algorithm, key, data);
|
||||||
|
if (hmacEncodeBytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return HexStringUtil.byteArrayToHexString(hmacEncodeBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Helper.HMac;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class HexStringUtil {
|
||||||
|
private static final byte[] HEX_CHAR_TABLE = {
|
||||||
|
(byte) '0', (byte) '1', (byte) '2', (byte) '3',
|
||||||
|
(byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||||
|
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
|
||||||
|
(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'
|
||||||
|
};
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a byte array to a hexadecimal string
|
||||||
|
*
|
||||||
|
* @param raw
|
||||||
|
* A raw byte array
|
||||||
|
*
|
||||||
|
* @return Hexadecimal string
|
||||||
|
*/
|
||||||
|
public static String byteArrayToHexString(byte[] raw) {
|
||||||
|
byte[] hex = new byte[2 * raw.length];
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (byte b : raw) {
|
||||||
|
int v = b & 0xFF;
|
||||||
|
hex[index++] = HEX_CHAR_TABLE[v >>> 4];
|
||||||
|
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
|
||||||
|
}
|
||||||
|
return new String(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a hexadecimal string to a byte array
|
||||||
|
*
|
||||||
|
* @param hex
|
||||||
|
* A hexadecimal string
|
||||||
|
*
|
||||||
|
* @return The byte array
|
||||||
|
*/
|
||||||
|
public static byte[] hexStringToByteArray(String hex) {
|
||||||
|
String hexstandard = hex.toLowerCase(Locale.ENGLISH);
|
||||||
|
int sz = hexstandard.length() / 2;
|
||||||
|
byte[] bytesResult = new byte[sz];
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
bytesResult[i] = (byte) (hexstandard.charAt(idx));
|
||||||
|
++idx;
|
||||||
|
byte tmp = (byte) (hexstandard.charAt(idx));
|
||||||
|
++idx;
|
||||||
|
|
||||||
|
if (bytesResult[i] > HEX_CHAR_TABLE[9]) {
|
||||||
|
bytesResult[i] -= ((byte) ('a') - 10);
|
||||||
|
} else {
|
||||||
|
bytesResult[i] -= (byte) ('0');
|
||||||
|
}
|
||||||
|
if (tmp > HEX_CHAR_TABLE[9]) {
|
||||||
|
tmp -= ((byte) ('a') - 10);
|
||||||
|
} else {
|
||||||
|
tmp -= (byte) ('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesResult[i] = (byte) (bytesResult[i] * 16 + tmp);
|
||||||
|
}
|
||||||
|
return bytesResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.capstone.foodify.ZaloPay.Helper;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
|
import com.capstone.foodify.ZaloPay.Helper.HMac.HMacUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Helpers {
|
||||||
|
private static int transIdDefault = 1;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
public static String getAppTransId() {
|
||||||
|
if (transIdDefault >= 100000) {
|
||||||
|
transIdDefault = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
transIdDefault += 1;
|
||||||
|
@SuppressLint("SimpleDateFormat") SimpleDateFormat formatDateTime = new SimpleDateFormat("yyMMdd_hhmmss");
|
||||||
|
String timeString = formatDateTime.format(new Date());
|
||||||
|
return String.format("%s%06d", timeString, transIdDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static String getMac(@NotNull String key, @NotNull String data) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
return Objects.requireNonNull(HMacUtil.HMacHexStringEncode(HMacUtil.HMACSHA256, key, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user