Våra sajter
Litium Studio Knowledgecenter

Knowledge Center för Litium Studio

Litium Scensum Knowledge Center

Knowledge Center för Litium Scensum

Litium Partner

Återfinns information som partner till Litium.

Support

Automate Payment Completion

There are two ways you can automate the collection of payment in a two phased (reserve - charge) payment mode scenario.

  1. You can write a scheduled task, which go through each order and decide whether payment should be collected.
  2. You can rely on the state transition system, to collect payment when delivery state changes.


In this article, we will look into the following scenario, which describes item 2 above.

  1. The web site is setup using the Payment mode "Reserve", which means we complete the payment transaction in two phases. In Phase 1, we reserve money from customer account, in phase 2 we charge the actual amount to customer account.
     
  2. The phase 1 will be completed by the normal checkout flow, and the money is reserved from customers account.
     
  3. The phase 2, the "Charge" customer account operation will be automated using the state transition of delivery into "Delivered" state.
     

What this means is, that administrator of the ECommerce module does not have to mannually go into each order, and then goto its payment and click the "Charge" button. Instead, he will go into the Delivery view, look into delivereies that is being made, and then change the status into "Delivered". This will automatically collect the payments for the delivered items. 

Prerequisites

This article is a continuation of the customizaiton of state transition engine. We are going to take the customization a further step by introducing a automated payment provider call, when the delivery state changes to "Delivered". Therefore, you should first complete the customization of state transition introduced here.

Step 1

Go to Litium Studio administration, and in your CMS module, under your website, select your checkout template, and click edit. In the settings tab, makesure the selected payment mode is "Reserve". This will make the transactions to be in "Reserved" state in the payment provider.

Step 2

Add the following code to the action taken when delivery goes into "Delivered" state. Note that we assume there is only one delivery in this scenario. If you have multiple deliveries for each order, we need additional checks.

/// <summary>

/// Called when delivery is confirmed.

/// </summary>

/// <param name="carrier">The carrier.</param>

/// <param name="state">The state.</param>

/// <param name="token">The token.</param>

protected virtual void OnDeliveryConfirmation(DeliveryCarrier carrier,

                    State<DeliveryCarrier> state, SecurityToken token)

{

    Order order = ModuleECommerce.Instance.Orders.GetOrder(carrier.OrderID, token);

    if (order != null)

    {

        PaymentInfo paymentInfo = order.PaymentInfo[0];

        if (paymentInfo.PaymentProvider.CanCompleteCurrentTransaction)

        {

            paymentInfo.PaymentProvider.CompletePayment(null, token);

        }

    }

}

 

Now, if you try to run your code, you should get a Exception, detailing that some one modified the order, a concurrency exception!.

This is expected, because, observe above, that you already have delivery carrier, whish should be part of a order carrier which is given to you by the state machine. That means, the state machine has its own internal instance of the order.

But, what you just did with the OnDeliveryConfirmation method is to take the same order directly from the database, and use it to complete the payment. The Payment Statemachine will possibly put the order into completed state and therefore change it. But the payment state machine is changing the order that you directly fetched from database, not the one that is being given by delivery state machine. When the delivery state machine try to do the same order state change, it will realize that it has an old order and will fail!.

To avoid this, lets not allow the state machine to change the order state if the carrier is outdated. To do that, lets add code below.

Step 3

Add a method to check whether order carrier is out-dated.

/// <summary>

/// Determines whether order is modified by a different process.

/// </summary>

/// <param name="carrier">The carrier.</param>

/// <param name="token">The token.</param>

/// <returns>

///    <c>true</c> if order modified; otherwise, <c>false</c>.

/// </returns>

private static bool IsOrderModified(OrderCarrier carrier, SecurityToken token)

{

    bool orderModified = false;

    Order order = ModuleECommerce.Instance.Orders.GetOrder(carrier.ID, token);

    if (order != null)

    {

        orderModified = carrier.LastUpdatedDate != order.LastUpdatedDate;

    }

    return orderModified;

}

 

Step 4

Now call this method in Order state transitions, and only allow the transition when order is not modified. Modify your transition conditions as below, shown in bold letters.

/// <summary>

/// Check whether an order can move from current state to next

/// </summary>

/// <param name="carrier">Carrier to be examined</param>

/// <param name="current">Current state.</param>

/// <param name="next">Next state.</param>

/// <param name="token">Security token.</param>

/// <returns>True if order can move, False otherwise.</returns>

protected virtual bool CanOrderMoveToCompleteState(OrderCarrier carrier, State<OrderCarrier> current,

                                                    State<OrderCarrier> next, SecurityToken token)

{

    //Check whether all deliveries are delivered.

    bool delivered = true;

    foreach (DeliveryCarrier delivery in carrier.Deliveries)

    {

        if (!delivery.CarrierState.IsMarkedForDeleting

            && delivery.DeliveryStatus != (short)DeliveryState.Delivered)

        {

            delivered = false;

            break;

        }

    }

 

    //Check whether all payments are paid

    bool paid = true;

    foreach (PaymentInfoCarrier payment in carrier.PaymentInfo)

    {

        if (!payment.CarrierState.IsMarkedForDeleting

            && payment.PaymentStatus != (short)PaymentStatus.Paid)

        {

            paid = false;

            break;

        }

    }

 

    // since the delivery state transition to "Delivered" take the order

    // from database and modify and save it

    // as part of the CompletePayment process, now

    // we should examine whether order is modified outside and stop

    // trying to save it if it is modified.

    return delivered && paid && !IsOrderModified(carrier, token);

}

 

And also when moving to Processing..

/// <summary>

/// Check whether an order can move from current state to next

/// </summary>

/// <param name="carrier">Carrier to be examined</param>

/// <param name="current">Current state.</param>

/// <param name="next">Next state.</param>

/// <param name="token">Security token.</param>

/// <returns>True if order can move, False otherwise.</returns>

protected virtual bool CanOrderMoveToProcessingState(OrderCarrier carrier, State<OrderCarrier> current,

                                                    State<OrderCarrier> next, SecurityToken token)

{

    //Check whether atleast one delivery is delivered.

    bool deliveriesStarted = carrier.Deliveries.Find(

        x => (!x.CarrierState.IsMarkedForDeleting) && x.DeliveryStatus == (short)DeliveryState.Delivered) != null;

 

    //Check whether atlease one payment is in paid or reserved state.

    bool paymentStarted = carrier.PaymentInfo.Find(x => (!x.CarrierState.IsMarkedForDeleting)

             && (x.PaymentStatus == (short)PaymentStatus.Paid
                || x.PaymentStatus == (short)PaymentStatus.Reserved)) != null;

 

    // since the delivery state transition to "Delivered" take the order

    // from database and modify and save it

    // as part of the CompletePayment process, now

    // we should examine whether order is modified outside and stop

    // trying to save it if it is modified.

    return (deliveriesStarted || paymentStarted) && !IsOrderModified(carrier,token);

}

 

Now you are ready to run your ECommerce website and place orders.

  1. Go to public website and place an order.
  2. Go to ECommerce administration, view the newly placed order statuses, its order status should be Processing, Payment Status should be Reserved and Delivery Status should be Init.
  3. Select the order, goto view deliveries, and change the delivery status to Delivered.
  4. Re-examin the order status and payment status. Order should be in Completed state and Payment should be in Paid state.

 

Note that...
For each order state transition attempted, we fetch the order from database. This might have performance problems.

 

 

 


Share this

| More

Comments made

I would rather add if(IsOrderModified(carrier,token)) return false;
first in the CanOrderMoveToProcessingState and CanOrderMoveToCompleteState
Comment by: Jonas Kastebo | 2012-02-16 15:34

Report

You need to be logged in to make a comment

Username*  


Password*  





Create your own user account!