Skip to content

PAYable SDK - Android Integration

Notifications You must be signed in to change notification settings

payable/payable-sdk-dev

Repository files navigation

PAYable SDK - Android Integration

Android SDK - android-sdk.payable.lk | Create Issue

Build Status


Initialization

  • Request and install Sandbox PAYable APP - Testing purpose

Initialization

  1. Add the below repository into your project level settings.gradle or build.gradle file.
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  1. Add the below dependency into your module level build.gradle file.
implementation 'com.github.payable:payable-sdk-dev:3.6.0'

Implementation

1. Import PAYable SDK packages.

import com.payable.sdk.Payable;
import com.payable.sdk.PayableListener;
import com.payable.sdk.PayableProgressListener;
import com.payable.sdk.PayableSale;

2. Implement PayableListener and declare PAYable client in your class.

public class MainActivity extends AppCompatActivity implements PayableListener {
    
    Payable payableClient;
    
    @Override
    boolean onPaymentStart(PayableSale payableSale){
        return true;
    }
    
    @Override
    void onPaymentSuccess(PayableSale payableSale){
        
    }
    
    @Override
    void onPaymentFailure(PayableSale payableSale){
        
    }
}

3. Create PAYable client with

Payable.createPayableClient(activity: Activity, client_id: String, client_name: String, api_key: String);

It should be declared inside onCreate method like below.

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    payableClient = Payable.createPayableClient(this, "1452", "FOOD_COURT", "C6DFA0B215B2CF24EF04794F718A3FC8");
}

4. Override onActivityResult method and set the callback listener to handle the response.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    ...
    payableClient.handleResponse(requestCode, data);
}

5. On click listener call the method to start payment.

PayableSale payableSale = new PayableSale(sale_amount: Double, payment_method: Integer);
  • Optional parameters ReadMe
payableSale.setReceiptEmail("[email protected]");
payableSale.setReceiptSMS("0110000000");
payableSale.setOrderTracking("invoice56");
payableSale.setTerminalId("1254");
  • Start the payment intent
payableClient.startPayment(payableSale, this);
  • Payment methods
Payable.METHOD_ANY
Payable.METHOD_CARD
Payable.METHOD_WALLET

Example:

  • For the order tracking you need to pass the tracking number in json data as below.
PayableSale payableSale = new PayableSale( /* saleAmount */ 500, Payable.METHOD_CARD);
payableSale.setReceiptEmail("[email protected]");
payableSale.setReceiptSMS("0110000000");
payableSale.setOrderTracking("invoice56");

payableClient.startPayment(payableSale, /* PayableListener */ this);
  • In order to choose a TID for the transaction, you can pass the terminal ID.
payableSale.setTerminalId("terminalId");
* Return Payable Object
payable.getStatusCode();
payable.getSaleAmount();
payable.getCcLast4();
payable.getCardType();
payable.getTxId();
payable.getTerminalId();
payable.getMid();
payable.getIsEmv();
payable.getTxnStatus();
payable.getReceiptSMS();
payable.getReceiptEmail();
payable.getOrderTracking();
* Return Status Codes
Payable.PAYABLE_REQUEST_CODE : 3569;
Payable.PAYABLE_STATUS_SUCCESS : 222;
Payable.PAYABLE_STATUS_NOT_LOGIN : 555;
Payable.PAYABLE_STATUS_FAILED : 0;
Payable.PAYABLE_INVALID_AMOUNT : 999;
Payable.PAYABLE_APP_NOT_INSTALLED : 888;
* Card Actions
Payable.TXN_SWIPE : 0;
Payable.TXN_EMV : 1;
Payable.TXN_MANUAL : 2;
Payable.TXN_NFC : 3;

Advanced Usage

Background Progress Listener
  • If you want to receive the progress updates of the ongoing payment in background, you need to register progress listener using registerProgressListener(listener) and make sure you unregister the listener using unregisterProgressListener() method on activity onDestroy() method to avoid memory leakage.
payableClient.registerProgressListener(new PayableProgressListener() {

    @Override
    public void onCardInteraction(int action, PayableSale payableSale) {
        
    }

    @Override
    public void onPaymentAccepted(PayableSale payableSale) {
        
    }

    @Override
    public void onPaymentRejected(PayableSale payableSale) {

    }
});

Explanation for PayableProgressListener interface.

onCardInteraction(int action, PayableSale payableSale)
  • This method will be called in the background when the terminal listens to any card interactions such as ENV, SWIPE, and NFC, this will respond with your sale values and interacted action as Payable.EMV, Payable.SWIPE, Payable.NFC and -1 for any error on card interaction. You can get the error description using payableSale.getMessage() method.
onPaymentAccepted(PayableSale payableSale)
  • This method will be called in the background when the terminal accepts the card and proceed further.
onPaymentRejected(PayableSale payableSale)
  • This method will be called in the background when the terminal rejects the card or throws any errors from servers.
Unregister progress listener
@Override
protected void onDestroy() {
    super.onDestroy();
    payableClient.unregisterProgressListener();
}
Register event listener

When you need to request any event from PAYable you will have to register the event listener and unregister it on onDestroy method when you are done.

payableClient.registerEventListener(new PayableEventListener() {

    @Override
    public void onProfileList(List<PayableProfile> payableProfiles) {
        
    }

    @Override
    public void onVoid(PayableResponse payableResponse) {
        
    }
});
Unregister event listener
@Override
protected void onDestroy() {
    super.onDestroy();
    payableClient.unregisterEventListener();
}
PAYable events
Method Callback
boolean requestProfileList() onProfileList(List<PayableProfile> payableProfiles)
boolean requestVoid(String txId, int cardType); onVoid(PayableResponse payableResponse)
boolean requestTransactionStatus(String txId, int cardType) onTransactionStatus(PayableTxStatusResponse payableResponse)
boolean requestTransactionStatusV2(String orderId, int cardType) onTransactionStatus(PayableTxStatusResponseV2 payableResponse)

  • PayableProfile
String tid;
String name;
String currency;
Integer installment;
  • PAYableResponse
int status;
String txId;
String error;
  • PayableTxStatusResponse
String cardName;
String ccLast4;
double amount;
int cardType;
String time;
String orderTracking;
int txType;
int currencyType;
int installment;
String tid;
String mid;
String cardNo;
  • PayableTxStatusResponseV2
String txKeyId
String cardHolder
String ccLast4
BigDecimal amount
int cardType
Date serverTime
String approvalCode
int transactionStatus

Example Usage
public class MainActivity extends AppCompatActivity implements PayableListener {

    EditText edtAmount, edtTracking, edtEmail, edtSMS, edtTxnId;
    Button btnPayCard, btnPayWallet, btnPay, btnProfile, btnVoid, btnStatus;
    TextView txtResponse, actTitle;

    double saleAmount = 0;
    String selectedProfile;

    // 1. Declare Payable Client
    Payable payableClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        edtAmount = findViewById(R.id.edtAmount);
        edtTracking = findViewById(R.id.edtTracking);
        edtEmail = findViewById(R.id.edtEmail);
        edtSMS = findViewById(R.id.edtSMS);
        edtTxnId = findViewById(R.id.edtTxnId);
        btnPayCard = findViewById(R.id.btnPayCard);
        btnPayWallet = findViewById(R.id.btnPayWallet);
        btnPay = findViewById(R.id.btnPay);
        btnProfile = findViewById(R.id.btnProfile);
        btnVoid = findViewById(R.id.btnVoid);
        btnStatus = findViewById(R.id.btnStatus);
        txtResponse = findViewById(R.id.txtResponse);
        actTitle = findViewById(R.id.actTitle);
        actTitle.setText("Main Activity");

        edtAmount.setFilters(AmountInputFilter.getFilter(this, 100000));

        // 2. Set Payable Client
        payableClient = Payable.createPayableClient(this, "1452", "FOOD_COURT", "C6DFA0B215B2CF24EF04794F718A3FC8");

        btnPayCard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                hideSoftKeyboard(edtAmount);

                // 3. Call your method
                payableSale(Payable.METHOD_CARD);
            }
        });

        btnPayWallet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                hideSoftKeyboard(edtAmount);

                // 3. Call your method
                payableSale(Payable.METHOD_WALLET);
            }
        });

        btnPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                payableSale(Payable.METHOD_ANY);
            }
        });

        btnProfile.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                payableClient.requestProfileList();
            }
        });

        /**
         * Advanced Usage (Optional):
         * If you want to receive the progress updates of the ongoing payment, you need to register a progress listener
         * and make sure you unregister the listener using unregisterProgressListener() method on activity onDestroy() method
         *
         */
        payableClient.registerProgressListener(new PayableProgressListener() {

            @Override
            public void onCardInteraction(int action, PayableSale payableSale) {
                Log.e("TEST_IMPL", "background: onCardInteraction: " + action + " => " + payableSale.toString());
                updateTxtResponse("background: onCardInteraction => " + action);
                Toast.makeText(getApplicationContext(), "background: onCardInteraction", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPaymentAccepted(PayableSale payableSale) {
                Log.e("TEST_IMPL", "background: onPaymentAccepted: " + payableSale.toString());
                updateTxtResponse("background: onPaymentAccepted => " + payableSale.getTxnTypeName());
                Toast.makeText(getApplicationContext(), "background: onPaymentAccepted", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPaymentRejected(PayableSale payableSale) {
                Log.e("TEST_IMPL", "background: onPaymentRejected => " + payableSale.toString());
                updateTxtResponse("background: onPaymentRejected: " + payableSale.getMessage());
                Toast.makeText(getApplicationContext(), "background: onPaymentRejected", Toast.LENGTH_SHORT).show();
            }
        });

        /**
         * Advanced Usage (Optional):
         * If you want to make any requests to PAYable and get responses, register the event listener
         * and make sure you unregister the listener using unregisterEventListener() method on activity onDestroy() method
         *
         */
        payableClient.registerEventListener(new PayableEventListener() {
            @Override
            public void onProfileList(final List<PayableProfile> payableProfiles) {

                for (PayableProfile payableProfile : payableProfiles) {
                    updateFreshTxtResponse("tid: " + payableProfile.tid + " " + payableProfile.currency + " name: " + payableProfile.name + " inst: " + payableProfile.installment);
                }

                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
                builder.setTitle("Select Profile");

                String[] profileNames = new String[payableProfiles.size()];
                for (int i = 0; i < payableProfiles.size(); i++) {
                    profileNames[i] = "tid: " + payableProfiles.get(i).tid + " " + payableProfiles.get(i).currency + " : name: " + payableProfiles.get(i).name + " inst: " + payableProfiles.get(i).installment;
                }

                builder.setItems(profileNames, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        selectedProfile = payableProfiles.get(which).tid;
                        btnProfile.setText("Selected Profile: " + selectedProfile);
                    }
                });

                AlertDialog dialog = builder.create();
                dialog.show();
            }

            @Override
            public void onVoid(PayableResponse payableResponse) {
                updateFreshTxtResponse("onVoid: " + payableResponse.status + " txId: " + payableResponse.txId + " error: " + payableResponse.error);
            }

            @Override
            public void onTransactionStatus(PayableTxStatusResponse payableResponse) {
                if (payableResponse.error != null) {
                    updateFreshTxtResponse("onTransactionStatus: " + payableResponse.status + " txId: " + payableResponse.txId + " error: " + payableResponse.error);
                } else {
                    updateFreshTxtResponse("onTransactionStatus: " + payableResponse.toString());
                }
            }

            @Override
            public void onTransactionStatusV2(PayableTxStatusResponseV2 payableResponse) {
                if (payableResponse.error != null) {
                    updateFreshTxtResponse("onTransactionStatus: " + payableResponse.status + " txId: " + " error: " + payableResponse.error);
                } else {
                    updateFreshTxtResponse("onTransactionStatus: " + payableResponse.toFormattedString());
                }
            }
        });

        btnVoid.setOnClickListener(v -> {
            if (!edtTxnId.getText().toString().isEmpty()) {
                Picker.cardTypePicker(MainActivity.this, cardType -> payableClient.requestVoid(edtTxnId.getText().toString(), cardType));
            }
        });

        btnStatus.setOnClickListener(v -> {
            if (!edtTxnId.getText().toString().isEmpty()) {
                Picker.cardTypePicker(MainActivity.this, cardType -> payableClient.requestTransactionStatus(edtTxnId.getText().toString(), cardType));
            }
        });

        btnStatusV2.setOnClickListener(v -> {
            if (!edtOrderId.getText().toString().isEmpty()) {
                Picker.cardTypePicker(MainActivity.this, cardType -> payableClient.requestTransactionStatusV2(edtOrderId.getText().toString(), cardType));
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        payableClient.unregisterProgressListener();
        payableClient.unregisterEventListener();
    }

    private void payableSale(int paymentMethod) {

        if (edtAmount.getText().toString().isEmpty()) {
            Toast.makeText(this, "Amount is empty", Toast.LENGTH_SHORT).show();
            return;
        }

        // 4. Convert sale amount to double from EditText
        saleAmount = Double.parseDouble(edtAmount.getText().toString());

        // 5. start the payment request to PAYable app with the callback listener
        PayableSale payableSale = new PayableSale(saleAmount, paymentMethod);

        if (!edtEmail.getText().toString().isEmpty()) {
            payableSale.setReceiptEmail(edtEmail.getText().toString());
        }

        if (!edtSMS.getText().toString().isEmpty()) {
            payableSale.setReceiptSMS(edtSMS.getText().toString());
        }

        if (!edtTracking.getText().toString().isEmpty()) {
            payableSale.setOrderTracking(edtTracking.getText().toString());
        }

        if (selectedProfile != null) {
            payableSale.setTerminalId(selectedProfile);
        }

        payableClient.startPayment(payableSale, this);

        // Deprecated implementations
        // payableClient.startPayment(saleAmount, paymentMethod, "{ \"ORDER_TRACKING\" : \"SDK-TEST\" }", this);
        // payableClient.startPayment(saleAmount, paymentMethod, this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // 7. onActivityResult set the callback listener to handle the response
        payableClient.handleResponse(requestCode, data);
    }

    // 8. onPaymentSuccess method
    @Override
    public boolean onPaymentStart(PayableSale payableSale) {
        txtResponse.setText("foreground: onPaymentStart => " + payableSale.getSaleAmount());
        return true;
    }

    // 8. onPaymentSuccess method
    @Override
    public void onPaymentSuccess(PayableSale payableSale) {
        updateTxtResponse("foreground: onPaymentSuccess => " + payableSale.getTxId());
        updateTxtResponse(payableSale);

        edtTxnId.setText(payableSale.getTxId());
    }

    // 9. onPaymentFailure method
    @Override
    public void onPaymentFailure(PayableSale payableSale) {
        updateTxtResponse("foreground: onPaymentFailure => " + payableSale.getMessage());
        updateTxtResponse(payableSale);
    }

    // 10. Update..
    private void updateTxtResponse(PayableSale payableSale) {

        String responseText = "\nstatusCode: " + payableSale.getStatusCode() + "\n";
        responseText += "responseAmount: " + payableSale.getSaleAmount() + "\n";
        responseText += "ccLast4: " + payableSale.getCcLast4() + "\n";
        responseText += "cardNo: " + payableSale.getCardNo() + "\n";
        responseText += "cardType: " + payableSale.getCardType() + "\n";
        responseText += "txId: " + payableSale.getTxId() + "\n";
        responseText += "terminalId: " + payableSale.getTerminalId() + "\n";
        responseText += "mid: " + payableSale.getMid() + "\n";
        responseText += "txnType: " + payableSale.getTxnType() + "\n";
        responseText += "txnStatus: " + payableSale.getTxnStatus() + "\n";
        responseText += "receiptSMS: " + payableSale.getReceiptSMS() + "\n";
        responseText += "receiptEmail: " + payableSale.getReceiptEmail() + "\n";
        responseText += "paymentMethod: " + payableSale.getPaymentMethod() + "\n";
        responseText += "message: " + payableSale.getMessage() + "\n";
        responseText += "orderTracking: " + payableSale.getOrderTracking() + "\n";

        updateTxtResponse(responseText);
    }

    private void updateTxtResponse(String message) {
        txtResponse.setText(txtResponse.getText().toString() + "\n" + message);
    }

    private void updateFreshTxtResponse(String message) {
        txtResponse.setText("");
        updateTxtResponse(message);
    }

    protected void hideSoftKeyboard(EditText input) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(input.getWindowToken(), 0);
    }
}

PAYable SDK Android Integration