Add ZaloPay method

This commit is contained in:
phanhuuloi27
2022-06-28 21:22:28 +07:00
parent 597e087f13
commit e0ee6f1f8a
18 changed files with 970 additions and 59 deletions
+3
View File
@@ -43,6 +43,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
implementation fileTree(dir: 'D:\\ZaloPay', include: ['*.aar', '*.jar'], exclude: [])
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
@@ -68,6 +69,8 @@ dependencies {
implementation 'com.github.rey5137:material:1.2.4'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
implementation("commons-codec:commons-codec:1.14")
implementation fileTree(dir: 'D:\\ZaloPay', includes: ['*.aar', '*.jar'], excludes: [])
implementation 'io.github.inflationx:calligraphy3:3.1.1'
implementation 'io.github.inflationx:viewpump:2.0.3'
+15
View File
@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
@@ -19,6 +20,20 @@
android:theme="@style/Theme.Foodify"
tools:replace="android:theme"
tools:targetApi="31">
<activity
android:name=".OrderDetail"
android:exported="true"
android:launchMode="singleTask">
<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
android:name=".ShowComment"
android:exported="false" />
@@ -3,6 +3,7 @@ package com.waterbase.foodify;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
@@ -314,20 +315,24 @@ public class Cart extends AppCompatActivity implements GoogleApiClient.Connectio
txtTotalPrice.getText().toString(),
"0",
edtComment.getText().toString(),
"Chưa thanh toán!",
cart
);
//Pass object to another activity!
Intent intent = new Intent(Cart.this, OrderDetail.class);
intent.putExtra("requests", request);
startActivity(intent);
//Summit to Firebase
//We will using System.CurrentMilli to key
String order_number = String.valueOf(System.currentTimeMillis());
requests.child(order_number).setValue(request);
// String order_number = String.valueOf(System.currentTimeMillis());
// requests.child(order_number).setValue(request);
//
// sendNotificationOrder(order_number);
sendNotificationOrder(order_number);
//Delete Cart
new Database(getBaseContext()).cleanCart();
Toast.makeText(Cart.this, "Đặt hàng thành công!", Toast.LENGTH_SHORT).show();
finish();
}
});
@@ -341,51 +346,7 @@ public class Cart extends AppCompatActivity implements GoogleApiClient.Connectio
alertDialog.show();
}
private void sendNotificationOrder(String order_number) {
DatabaseReference tokens = FirebaseDatabase.getInstance().getReference("Tokens");
Query data = tokens.orderByChild("serverToken").equalTo(true); //Get all node with isServerToken is true;
data.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapShot : dataSnapshot.getChildren()) {
Token serverToken = postSnapShot.getValue(Token.class);
//Create raw payload to send
Notification notification = new Notification("Foodify", "Bạn có 1 đơn hàng mới " + order_number);
Sender content = new Sender(serverToken.getToken(), notification);
mService.sendNotification(content)
.enqueue(new Callback<MyResponse>() {
@Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
//Only run when get result
if (response.code() == 200) {
if (response.body().success == 1) {
new Database(getBaseContext()).cleanCart();
Toast.makeText(Cart.this, "Đặt hàng thành công!", Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(Cart.this, "Hệ thống bị lỗi. Vui lòng thử lại sau!", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onFailure(Call<MyResponse> call, Throwable t) {
Log.e("ERROR", t.getMessage());
}
});
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void loadListFood() {
cart = new Database(this).getCarts();
@@ -1,6 +1,8 @@
package com.waterbase.foodify.Model;
public class Order {
import java.io.Serializable;
public class Order implements Serializable {
private int ID;
private String ProductId;
@@ -1,26 +1,29 @@
package com.waterbase.foodify.Model;
import java.io.Serializable;
import java.util.List;
public class Request {
public class Request implements Serializable {
private String phone;
private String name;
private String address;
private String total;
private String status;
private String comment;
private String paymentStatus;
private List<Order> foods; // list of food order
public Request() {
}
public Request(String phone, String name, String address, String total, String status, String comment, List<Order> foods) {
public Request(String phone, String name, String address, String total, String status, String comment, String paymentStatus, List<Order> foods) {
this.phone = phone;
this.name = name;
this.address = address;
this.total = total;
this.status = status;
this.comment = comment;
this.paymentStatus = paymentStatus;
this.foods = foods;
}
@@ -72,6 +75,14 @@ public class Request {
this.comment = comment;
}
public String getPaymentStatus() {
return paymentStatus;
}
public void setPaymentStatus(String paymentStatus) {
this.paymentStatus = paymentStatus;
}
public List<Order> getFoods() {
return foods;
}
@@ -0,0 +1,264 @@
package com.waterbase.foodify;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.button.MaterialButton;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import com.waterbase.foodify.Common.Common;
import com.waterbase.foodify.Database.Database;
import com.waterbase.foodify.Model.MyResponse;
import com.waterbase.foodify.Model.Notification;
import com.waterbase.foodify.Model.Order;
import com.waterbase.foodify.Model.Request;
import com.waterbase.foodify.Model.Sender;
import com.waterbase.foodify.Model.Token;
import com.waterbase.foodify.Remote.APIService;
import com.waterbase.foodify.ViewHolder.OrderDetailAdapter;
import com.waterbase.foodify.Zalopay.Api.CreateOrder;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
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 OrderDetail extends AppCompatActivity {
TextView order_address, order_total, order_comment;
Request request;
RecyclerView lstFoods;
RecyclerView.LayoutManager layoutManager;
MaterialButton btnPay, btnZaloPay;
FirebaseDatabase database;
DatabaseReference requests;
APIService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_order_detail);
String comment = "";
setTitle("Thông tin chi tiết đơn hàng");
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
// ZaloPay SDK Init
ZaloPaySDK.init(2554, Environment.SANDBOX);
order_address = (TextView) findViewById(R.id.order_address);
order_total = (TextView) findViewById(R.id.order_total);
order_comment = (TextView) findViewById(R.id.order_comment);
btnPay = findViewById(R.id.btnPay);
btnZaloPay = findViewById(R.id.btnZaloPay);
lstFoods = (RecyclerView) findViewById(R.id.lstFoods);
lstFoods.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
lstFoods.setLayoutManager(layoutManager);
//Firebase
database = FirebaseDatabase.getInstance();
requests = database.getReference("Requests");
mService = Common.getFCMService();
if (getIntent() != null)
request = (Request) getIntent().getSerializableExtra("requests");
//Set value
order_total.setText("Tổng cộng: " + request.getTotal());
order_address.setText("Địa chỉ giao: " + request.getAddress());
if (TextUtils.isEmpty(request.getComment()))
comment = "Không có";
else
comment = request.getComment();
order_comment.setText("Ghi chú: " + comment);
OrderDetailAdapter adapter = new OrderDetailAdapter(request.getFoods(), this);
adapter.notifyDataSetChanged();
lstFoods.setAdapter(adapter);
btnPay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Summit to Firebase
//We will using System.CurrentMilli to key
String order_number = String.valueOf(System.currentTimeMillis());
requests.child(order_number).setValue(request);
sendNotificationOrder(order_number);
//Delete Cart
new Database(getBaseContext()).cleanCart();
Toast.makeText(OrderDetail.this, "Đặt hàng thành công!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(OrderDetail.this, Home.class));
finish();
}
});
btnZaloPay.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@SuppressLint("SetTextI18n")
@Override
public void onClick(View v) {
String totalPrice = request.getTotal().substring(0, request.getTotal().length() - 2).replace(".", ""); //Remove character " đ"
CreateOrder orderApi = new CreateOrder();
try {
JSONObject data = orderApi.createOrder(totalPrice);
Log.d("Amount", totalPrice);
String code = data.getString("return_code");
if (code.equals("1")) {
String token = data.getString("zp_trans_token");
ZaloPaySDK.getInstance().payOrder(OrderDetail.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() {
request.setPaymentStatus("Đã thanh toán!");
//Summit to Firebase
//We will using System.CurrentMilli to key
String order_number = String.valueOf(System.currentTimeMillis());
requests.child(order_number).setValue(request);
sendNotificationOrder(order_number);
//Delete Cart
new Database(getBaseContext()).cleanCart();
Toast.makeText(OrderDetail.this, "Đặt hàng thành công!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(OrderDetail.this, Home.class));
finish();
}
});
}
@Override
public void onPaymentCanceled(String zpTransToken, String appTransID) {
Toast.makeText(OrderDetail.this, "Bạn đã huỷ thanh toán!", Toast.LENGTH_SHORT).show();
}
@Override
public void onPaymentError(ZaloPayError zaloPayError, String zpTransToken, String appTransID) {
Toast.makeText(OrderDetail.this, "Đã xảy ra lỗi hệ thống! Xin vui lòng thử lại sau!", Toast.LENGTH_SHORT).show();
}
});
}
} catch (Exception e) {
Log.e("ERROR", "" + e.getMessage());
}
}
});
// calling the action bar
ActionBar actionBar = getSupportActionBar();
// showing the back button in action bar
actionBar.setDisplayHomeAsUpEnabled(true);
}
private void sendNotificationOrder(String order_number) {
DatabaseReference tokens = FirebaseDatabase.getInstance().getReference("Tokens");
Query data = tokens.orderByChild("serverToken").equalTo(true); //Get all node with isServerToken is true;
data.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapShot : dataSnapshot.getChildren()) {
Token serverToken = postSnapShot.getValue(Token.class);
//Create raw payload to send
Notification notification = new Notification("Foodify", "Bạn có 1 đơn hàng mới " + order_number);
Sender content = new Sender(serverToken.getToken(), notification);
mService.sendNotification(content)
.enqueue(new Callback<MyResponse>() {
@Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
//Only run when get result
if (response.code() == 200) {
if (response.body().success == 1) {
new Database(getBaseContext()).cleanCart();
Toast.makeText(OrderDetail.this, "Đặt hàng thành công!", Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(OrderDetail.this, "Hệ thống bị lỗi. Vui lòng thử lại sau!", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onFailure(Call<MyResponse> call, Throwable t) {
Log.e("ERROR", t.getMessage());
}
});
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
ZaloPaySDK.getInstance().onResult(intent);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
@@ -94,7 +94,7 @@ public class CartAdapter extends RecyclerView.Adapter<CartViewHolder>{
holder.btn_quantity.setOnValueChangeListener(new ElegantNumberButton.OnValueChangeListener() {
@Override
public void onValueChange(ElegantNumberButton view, int oldValue, int newValue) {
Order order = listData.get(position);
Order order = listData.get(holder.getAdapterPosition());
order.setQuantity(String.valueOf(newValue));
new Database(cart).updateCart(order);
@@ -0,0 +1,83 @@
package com.waterbase.foodify.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.picasso.Picasso;
import com.waterbase.foodify.Cart;
import com.waterbase.foodify.Model.Order;
import com.waterbase.foodify.OrderDetail;
import com.waterbase.foodify.R;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name, quantity, price, discount;
public ImageView cart_image;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.product_name);
quantity = (TextView) itemView.findViewById(R.id.product_quantity);
price = (TextView) itemView.findViewById(R.id.product_price);
discount = (TextView) itemView.findViewById(R.id.product_discount);
cart_image = (ImageView) itemView.findViewById(R.id.cart_image);
}
}
public class OrderDetailAdapter extends RecyclerView.Adapter<MyViewHolder>{
List<Order> myOrders;
private OrderDetail orderDetail;
public OrderDetailAdapter(List<Order> myOrders, OrderDetail orderDetail) {
this.myOrders = myOrders;
this.orderDetail = orderDetail;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.order_detail_layout, parent, false);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
double totalPrice;
Order order = myOrders.get(position);
Picasso.with(orderDetail.getBaseContext())
.load(myOrders.get(position).getImage())
.resize(70,70)
.centerCrop()
.into(holder.cart_image);
totalPrice = Double.parseDouble(order.getQuantity()) * Double.parseDouble(order.getPrice());
Locale locale = new Locale("vi", "VN");
NumberFormat fmt = NumberFormat.getCurrencyInstance(locale);
holder.name.setText(String.format("Món ăn: %s", order.getProductName()));
holder.quantity.setText(String.format("Số lượng: %s", order.getQuantity()));
holder.price.setText("Giá: " + fmt.format(totalPrice));
holder.discount.setText(String.format("Giảm giá: %s", order.getDiscount()));
}
@Override
public int getItemCount() {
return myOrders.size();
}
}
@@ -0,0 +1,70 @@
package com.waterbase.foodify.Zalopay.Api;
import com.waterbase.foodify.Zalopay.Constant.AppInfo;
import com.waterbase.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 = "Merchant pay for order #" + 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.waterbase.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.waterbase.foodify.Zalopay.Constant;
public class AppInfo {
public static final int APP_ID = 2554;
public static final String MAC_KEY = "sdngKKJmqEMzvh5QQcdD2A9XBSKUNaYn";
public static final String URL_CREATE_ORDER = "https://sb-openapi.zalopay.vn/v2/create";
}
@@ -0,0 +1,93 @@
package com.waterbase.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.waterbase.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.waterbase.foodify.Zalopay.Helper;
import android.annotation.SuppressLint;
import com.waterbase.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));
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -8,13 +8,25 @@
android:padding="16dp"
tools:context=".Cart">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listCart"
android:background="@android:color/transparent"
<LinearLayout
android:layout_above="@+id/cardView"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listCart"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_alignParentBottom="true"
app:cardBackgroundColor="@color/colorPrimary"
android:layout_width="match_parent"
@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg4"
tools:context=".OrderDetail">
<com.google.android.material.card.MaterialCardView
android:id="@+id/order_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="4dp"
>
<LinearLayout
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_weight="9"
android:layout_width="0dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/order_total"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="bold"
android:text="100000 VND"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/order_address"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="italic"
android:text="Address"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/order_comment"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="italic"
android:text="Comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/orderDetail"
android:layout_width="match_parent"
android:layout_height="450dp"
android:layout_below="@+id/order_info"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginBottom="20dp"
android:text="Chi tiết đơn:"
android:textColor="@color/white"
android:textSize="20sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lstFoods"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_below="@id/orderDetail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnZaloPay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_gravity="center"
android:textStyle="bold"
app:icon="@drawable/zalopayfix"
app:iconTint="@null"
app:iconGravity="textStart"
app:iconPadding="10dp"
android:gravity="center"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="Thanh toán bằng ZaloPay"
android:textColor="#0068FF"
android:backgroundTint="@color/white"
/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnPay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_gravity="center"
android:textStyle="bold"
app:iconTint="@null"
app:iconGravity="textStart"
app:iconPadding="10dp"
android:gravity="center"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="Thanh toán khi nhận hàng"
android:textColor="@color/white"
/>
</LinearLayout>
</RelativeLayout>
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/order_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="4dp"
>
<LinearLayout
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_weight="9"
android:layout_width="0dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/product_name"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="bold"
android:text="Product Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/product_quantity"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="italic"
android:text="Product Quantity"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/product_price"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="italic"
android:text="Product Price"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/product_discount"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|start"
android:textAllCaps="true"
android:textStyle="italic"
android:text="Product Discount"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/cart_image"
android:layout_marginRight="20dp"
android:layout_width="70dp"
android:layout_height="70dp"/>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>