mirror of
https://github.com/Nezumi-2711/PRM392.git
synced 2026-09-22 13:48:29 +00:00
Add ZaloPay method
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+69
@@ -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>
|
||||
Reference in New Issue
Block a user