Advanced 35 min read

Make It Asynchronous (Queueable)

By the end of this lesson, you'll be able to:

  • Wrap PaymentGatewayService in a Queueable job
  • Enqueue the job from a trigger handler
  • Update the Order record with the result from inside the Queueable job

Prerequisites: "Build the Callout Service"

The Queueable wrapper

public class ProcessPaymentJob implements Queueable {
    private Id orderId;
    private Decimal amount;
    private String paymentToken;

    public ProcessPaymentJob(Id orderId, Decimal amount, String paymentToken) {
        this.orderId = orderId;
        this.amount = amount;
        this.paymentToken = paymentToken;
    }

    public void execute(QueueableContext context) {
        PaymentResult result = new PaymentGatewayService().charge(amount, paymentToken);

        Order__c order = new Order__c(Id = orderId);
        if (result.success) {
            order.Status__c = 'Paid';
            order.Payment_Reference__c = result.gatewayTransactionId;
        } else if (result.declined) {
            order.Status__c = 'Payment Declined';
            order.Decline_Reason__c = result.message;
        } else {
            order.Status__c = 'Payment Error';
        }
        update order;
    }
}

This is Module 38's Queueable pattern doing genuinely real work: execute() calls the synchronous-looking PaymentGatewayService (which is fine now, since it's running in its own async transaction, not inside the original trigger), then updates the Order__c based on Lesson 2's typed result.

Enqueuing from the trigger handler

public class OrderTriggerHandler {
    public void afterUpdate(List<Order__c> newOrders, Map<Id, Order__c> oldMap) {
        for (Order__c order : newOrders) {
            Order__c oldOrder = oldMap.get(order.Id);
            Boolean justReadyForPayment = order.Status__c == 'Ready for Payment' && oldOrder.Status__c != 'Ready for Payment';

            if (justReadyForPayment) {
                System.enqueueJob(new ProcessPaymentJob(order.Id, order.Total__c, order.Payment_Token__c));
            }
        }
    }
}

This is Module 24's Trigger.oldMap transition-detection pattern (from Module 26's rollup) combined with Module 38's System.enqueueJob — the trigger itself never makes a callout at all, it only ever enqueues asynchronous work.

Exercise

As a comment, trace through why OrderTriggerHandler.afterUpdate never violates the "no synchronous callouts in a trigger" rule.

Show hint

Look at exactly what happens inside afterUpdate itself.

APEX

Make It Asynchronous (Queueable) Quiz

1. Where does the actual HTTP callout to the payment gateway happen?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

This lesson wraps Lesson 2's service in a Queueable job — Module 38's pattern, applied to make the synchronous-looking service actually usable from a trigger.