# Payment Processing

Poynt Payment Fragment provides a secure and consistent payment experience for merchants and consumers across the applications running on GoDaddy Poynt Smart Terminals.

Payment Fragments process payment transactions securely at the PoyntOS level so your applications don't have to handle multiple payment methods, payment processes and compliance requirements (e.g. PCI Compliance).

NOTE

All the applications running on GoDaddy Poynt terminals need to use the Payment Fragment for all transactions, including cash.

# Payment Code

Receiving a payment can be as simple as creating a payment object and starting the intent to launch the Payment Fragment.

Once the payment is complete you should get the result through the onActivityResult.

    Payment payment = new Payment();
    payment.setAmount(amount);
    payment.setCurrency(currencyCode);

    // start Payment activity for result
    try {
        Intent collectPaymentIntent = new Intent(Intents.ACTION_COLLECT_PAYMENT);
        collectPaymentIntent.putExtra(Intents.INTENT_EXTRAS_PAYMENT, payment);
        startActivityForResult(collectPaymentIntent, COLLECT_PAYMENT_REQUEST);
    } catch (ActivityNotFoundException ex) {
        Log.e(TAG, "Poynt payment activity not found - did you install PoyntServices?", ex);
    }

The payment result contains the transaction information. However, you must keep in mind there may be more than one transaction for the same payment.

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == COLLECT_PAYMENT_REQUEST){
            if (resultCode == Activity.RESULT_OK){
                final Payment payment = data.getParcelableExtra(Intents.INTENT_EXTRAS_PAYMENT);
                Log.d(TAG, "Received onPaymentAction from PaymentFragment w/ Status("
                        + payment.getStatus() + ")");
                // We just need to look at the payment status, the rest of the information is in the transaction object
                switch (payment.getStatus()) {
                    case COMPLETED:
                        if (payment.getTransactions() != null &&  payment.getTransactions().size() > 0) {
                            // Payment object has a list of transactions
                        }
                        break;
                    case CANCELED:
                            // Payment was cancelled
                        break;
                        ...
                        ...
                        ...
                }
            } else if(resultCode == Activity.RESULT_CANCELED){
                // Payment was cancelled by the user
            }
        }
    }

# Payment Customization

Payments can be customized according to the application requirements. If you would like to pre-set the tip or accept payment with debit cards only, these conditions can be configured by setting additional parameters on the payment object.

The payment object contains a series of parameters that allow developers to configure the payment experience. Below is a comprehensive list of the parameters that are currently supported.

# Payment Options Configuration

  • disableDebit (boolean) - Disables the debit card option.

  • disableCheck (boolean) - Disables the check option.

  • disableOther (boolean) - Disables the "other" option.

  • disableManual (boolean) - Disables the manual entry.

  • disableEMVCT (boolean) - Disables EMV (chip card) payments.

  • disableEMVCL (boolean) - Disables contactless payments.

  • disableMSR (boolean) - Disables payments with magstripe cards.

  • disableCash (boolean) - Disables the cash option.

# Funding Source Restriction

  • cashOnly (boolean) - Launches the Payment Fragment directly into the cash flow.

  • debitOnly (boolean) - Allows debit card payment only.

  • creditOnly (boolean) - Allows payment by credit only.

  • manualEntry (boolean) - Launches the Payment Fragment into manual card entry flow.

# Payment Flow - Skipping Screens

  • disableTip (boolean) - Disables the tipping feature if the merchant account is configured to present the tip screen

  • skipReceiptScreen (boolean) - Eliminates the receipt screen.

  • skipSignatureScreen (boolean) - Eliminates the signature screen.

  • skipPaymentConfirmationScreen (boolean) - Displays the processing screen as opposed to the "Thank You!" screen after a payment is complete.

# Additional Options

  • cashbackAmount (long) - Presets the cashback amount when customer pays by debit.

  • tipAmount (long) - Presets the tip amount. The tip selection screen will be skipped.

  • tipAmounts (Map) - This is preset by the Payment Fragment and contains the mapping between transactionId and tip amount. In case of a multi-tender transaction, this will allow leaving a tip on more than one transaction.

    TIP

    You should always look at the tipAmount in Transaction.amounts.tipAmount and use tipAmounts map as a fallback.

  • readCardDataOnly (boolean) - Stops the transaction process and only returns information about the card (e.g. last 4, first 6, name.)

  • offlineAuth (boolean) - Processes an offline transaction. This can be used if there is no network connectivity. Merchant will be prompted to provide an approval code they obtained from the issuing bank.

  • offlineApprovalCode (String) - The approval code can be passed in the request to launch Payment Fragment (This is entirely optional).

  • disablePaymentOptions (boolean) - Hides the “Summary”, “Notes” and “Receipt” options from Payment Fragment.

  • disableChangeAmount (boolean) - If Payment Fragment is invoked to perform a refund or capture, setting this flag to “true” will not allow the merchant to edit the amount so partial capture or partial refund will not be available.

  • disableChangeAmount (boolean) - If the payment fragment is called to perform a refund or capture, setting this flag to "true" will not allow the merchant to edit the amount (i.e. no partial capture or partial refund.)

  • notes (String) - Custom notes which will be added to the transaction.

  • references (List<TransactionReferences>) - Allows the application to pass custom reference information, such as custom orderId and other metadata. This list is returned inside the Transaction object once the payment is completed.

  • multiTender (boolean) - Starts the payment fragment in the multi-tender mode which allows the buyer to pay using 2 or more tenders.

  • authzOnly (boolean) - Indicates that this is a pre-auth transaction that will not be part of the open settlement batch until it is captured.

  • orderId (String) - Represents the ID of the Order object, which has already been created or will be created after the payment.

    Having an Order object allows the Payment Fragment to display item details and send the itemized email receipt if the customer chooses to get an email receipt.

  • order (Order) - Including the order object, it allows the Payment Fragment to print an itemized receipt if the customer chooses paper receipt option

  • nonReferencedCredit (boolean) - Setting this flag will allow the merchant to issue a credit to a card without having a prior sale.

    NOTE

    This is considered a high-risk permission. Typically, the merchant has to be enabled by the acquirer to issue any non-referenced credits.

  • readCardDataOnly (boolean) - Provides information about the card without processing a transaction (e.g. last 4, first 6, name)

  • offlineAuth (boolean) - Process offline transaction.

    NOTE

    This object can be used if there is no network connectivity. The merchant will be prompted to provide an approval code they obtained from the issuing bank.

  • offlineApprovalCode (String) - Optionally, the approval code can be passed in the request to launch Payment Fragment.

  • disablePaymentOptions (boolean) - hide the "Summary", "Notes" and "Receipt" options from the Payment Fragment.

# Transaction References

  • setReferences (List\<TransactionReference\> references) - Passes custom references that will be applied to transactions

# Orders

Poynt SDK provides support to create and manage orders. The order model is simple and supports a wide range of use cases, whether a restaurant or a retail store.

Model Reference

Order (opens new window)

# Creating Orders

# Order Items

The order item can be created from a product in the catalog

The code snippet shows how to create an order from the product.

Similar code can be used to create an order item from an external catalog.

    OrderItem item = new OrderItem();
    item.setName(product.getName());
    item.setProductId(product.getId());
    item.setUnitPrice(product.getPrice().getAmount());
    item.setQuantity(quantity);
    item.setDetails(product.getDescription());
    item.setSku(product.getSku());
    item.setStatus(OrderItemStatus.ORDERED);

Fees, discounts and taxes can be applied to an order item, each item brings a list for each of the attributes.

TIP

Feel free to check some of the related articles to learn more about these topics:

# Order Amounts

The order amount should be the sum total of all order amounts at the item level, and fees and discounts at the order level.

OrderAmounts amounts = new OrderAmounts();
for (OrderItem item : items){
    Float quantity = item.getQuantity();
    float itemPrice = (item.getUnitPrice() * item.getQuantity());
    float itemsfee = 0;

    // Add item Fees
    if (item.getFees() != null && item.getFees().size() > 0){
        for (Fee fee : item.getFees()){
            if (fee.getPercentage() != null){
                itemsfee += itemPrice * fee.getPercentage();
            }else {
                itemsfee += fee.getAmount();
            }
        }
        itemsFeeTotal = (long)itemsfee;
    }else{
        if(item.getFee() != null) {
            itemsFeeTotal += (long) (quantity * item.getFee());
        }
    }

    // Add item discounts
    if(item.getDiscount() != null) {
        itemsDiscountTotal -= item.getDiscount();
    }

    // Add item Taxes
    itemsTaxTotal += getItemTax(item);  // refer to the sample POS app

    itemsSubTotal += itemPrice;
}

// Set the totals to the amounts
amounts.setFeeTotal(itemsFeeTotal);
amounts.setDiscountTotal(itemsDiscountTotal);
amounts.setTaxTotal(itemsTaxTotal);
amounts.setSubTotal(itemsSubTotal);

itemsNetTotal = itemsSubTotal + itemsFeeTotal + itemsDiscountTotal + itemsTaxTotal;
amounts.setNetTotal(itemsNetTotal);

Create an order on the device and cloud environments by sending the order object created. The request takes the listener as an input to provide a result.

orderService.createOrder(order, UUID.randomUUID().toString(), createOrderListener);

Updating an order can be done in a similar way when the items are modified or new items need to be added. The order Id of the original order needs to be passed in to update the order.

orderService.updateOrder(orderId, order, UUID.randomUUID().toString(), updateOrderLister);

When the payment for an order has been received, the order can be closed by setting the associated transaction to it and calling the complete order.

order.setTransactions(payment.getTransactions());
orderService.completeOrder(orderId, order, UUID.randomUUID().toString(), completeOrderListener);

TIP

The item status needs to be updated to FULFILLED before marking an order as complete.

# Open Order Fetching

The orders created are also stored locally in the content providers on the device. The code snippet below shows the correct way to fetch an open order.

String[] mProjection = OrderstatusesColumns.FULL_PROJECTION;
String mSelectionClause = OrderstatusesColumns.FULFILLMENTSTATUS + "= ?";
String[] mSelectionArgs = {OrderStatus.OPENED.status()};
String mSortOrder = null;
Cursor cursor = getContentResolver().query(OrdersColumns.CONTENT_URI_WITH_NETTOTAL_TRXN_STATUS,
                mProjection, mSelectionClause, mSelectionArgs, mSortOrder);
OrdersCursor orderCursor = new OrdersCursor(cursor);
if (orderCursor != null) {
    if (orderCursor.getCount() > 0) {
        while (orderCursor.moveToNext()) {
            orderId = orderCursor.getOrderid();
            Log.d(TAG, "order id: " + orderId);
            Log.d(TAG, "customer user id: " + orderCursor.getCustomeruserid());
            Log.d(TAG, "order number: " + orderCursor.getOrdernumber());
        }
    }
    orderCursor.close();
    cursor.close();
}

# Collecting Customer Information

The second screen on GoDaddy Poynt smart terminals is not just for collecting card pins. There are also a set of pre-defined templates that can be used to interact with customers and collect other information.

Below is a simple snippet to collect the customer's email address

Bundle options = new Bundle();
options.putString(Intents.EXTRA_EMAIL, "Enter your email");
options.putString(EXTRA_LEFT_BUTTON_TITLE, "TAKE MY EMAIL");
options.putString(EXTRA_RIGHT_BUTTON_TITLE, "GIVE ME PRIVACY");
secondScreenService.captureEmail(options, new IPoyntEmailEntryListener.Stub() {
    @Override
    public void onEmailEntered(String s) throws RemoteException {
        Log.d(TAG, "Email entered : " + s);
    }
    @Override
    public void onCancel() throws RemoteException {
        Log.d(TAG, "Email entry cancelled");
    }
});

# Post-sale Operations

Performing operations on an existing transaction can be done using Payment Fragments.

You can start the fragment by passing the transaction ID with the display intent action.

NOTE

The options available could vary depending on the status of the transaction.

Intent intent = new Intent(Intents.ACTION_DISPLAY_PAYMENT);
intent.putExtra(Intents.INTENT_EXTRAS_TRANSACTION_ID, transactionId);
startActivity(intent);
Last Updated: 9/4/2023, 1:28:22 PM