diff --git a/Foodify/app/build.gradle b/Foodify/app/build.gradle index 0d8e469..0790b12 100644 --- a/Foodify/app/build.gradle +++ b/Foodify/app/build.gradle @@ -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' diff --git a/Foodify/app/src/main/AndroidManifest.xml b/Foodify/app/src/main/AndroidManifest.xml index 788325c..cac6fa1 100644 --- a/Foodify/app/src/main/AndroidManifest.xml +++ b/Foodify/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + + + + + + + + + + + diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Cart.java b/Foodify/app/src/main/java/com/waterbase/foodify/Cart.java index 6adb614..a8733fe 100644 --- a/Foodify/app/src/main/java/com/waterbase/foodify/Cart.java +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Cart.java @@ -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() { - @Override - public void onResponse(Call call, Response 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 call, Throwable t) { - Log.e("ERROR", t.getMessage()); - } - }); - } - } - - @Override - public void onCancelled(DatabaseError databaseError) { - - } - }); - } private void loadListFood() { cart = new Database(this).getCarts(); diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Model/Order.java b/Foodify/app/src/main/java/com/waterbase/foodify/Model/Order.java index 1bcb6fa..40a69a5 100644 --- a/Foodify/app/src/main/java/com/waterbase/foodify/Model/Order.java +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Model/Order.java @@ -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; diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Model/Request.java b/Foodify/app/src/main/java/com/waterbase/foodify/Model/Request.java index 7a8943e..41b6816 100644 --- a/Foodify/app/src/main/java/com/waterbase/foodify/Model/Request.java +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Model/Request.java @@ -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 foods; // list of food order public Request() { } - public Request(String phone, String name, String address, String total, String status, String comment, List foods) { + public Request(String phone, String name, String address, String total, String status, String comment, String paymentStatus, List 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 getFoods() { return foods; } diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/OrderDetail.java b/Foodify/app/src/main/java/com/waterbase/foodify/OrderDetail.java new file mode 100644 index 0000000..2a51289 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/OrderDetail.java @@ -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() { + @Override + public void onResponse(Call call, Response 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 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); + } +} \ No newline at end of file diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/CartAdapter.java b/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/CartAdapter.java index a31c804..a91ea3e 100644 --- a/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/CartAdapter.java +++ b/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/CartAdapter.java @@ -94,7 +94,7 @@ public class CartAdapter extends RecyclerView.Adapter{ 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); diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/OrderDetailAdapter.java b/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/OrderDetailAdapter.java new file mode 100644 index 0000000..96e7caa --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/ViewHolder/OrderDetailAdapter.java @@ -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{ + + List myOrders; + private OrderDetail orderDetail; + + public OrderDetailAdapter(List 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(); + } +} diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/CreateOrder.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/CreateOrder.java new file mode 100644 index 0000000..1c56f97 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/CreateOrder.java @@ -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; + } +} + diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/HttpProvider.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/HttpProvider.java new file mode 100644 index 0000000..d0ae765 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Api/HttpProvider.java @@ -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; + } +} diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Constant/AppInfo.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Constant/AppInfo.java new file mode 100644 index 0000000..569bdf0 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Constant/AppInfo.java @@ -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"; +} diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HMacUtil.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HMacUtil.java new file mode 100644 index 0000000..b619607 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HMacUtil.java @@ -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 HMACS = new LinkedList(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); + } +} diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HexStringUtil.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HexStringUtil.java new file mode 100644 index 0000000..22d01bc --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/HMac/HexStringUtil.java @@ -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; + } +} diff --git a/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/Helpers.java b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/Helpers.java new file mode 100644 index 0000000..c194235 --- /dev/null +++ b/Foodify/app/src/main/java/com/waterbase/foodify/Zalopay/Helper/Helpers.java @@ -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)); + } +} diff --git a/Foodify/app/src/main/res/drawable/zalopayfix.png b/Foodify/app/src/main/res/drawable/zalopayfix.png new file mode 100644 index 0000000..7b5c824 Binary files /dev/null and b/Foodify/app/src/main/res/drawable/zalopayfix.png differ diff --git a/Foodify/app/src/main/res/layout/activity_cart.xml b/Foodify/app/src/main/res/layout/activity_cart.xml index f900110..b6f543d 100644 --- a/Foodify/app/src/main/res/layout/activity_cart.xml +++ b/Foodify/app/src/main/res/layout/activity_cart.xml @@ -8,13 +8,25 @@ android:padding="16dp" tools:context=".Cart"> - + android:layout_height="match_parent" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Foodify/app/src/main/res/layout/order_detail_layout.xml b/Foodify/app/src/main/res/layout/order_detail_layout.xml new file mode 100644 index 0000000..0a195bc --- /dev/null +++ b/Foodify/app/src/main/res/layout/order_detail_layout.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file