开发者

Grails and PayPal (Express Checkout)

There are two PayPal plugins for Grails, the first I found is a year old and seems to be in well enough order. The second it a bit older and apparently not compatible with 1.1+.

If I decide to not use an existing plugin what is the best way to implement a simple Express Checkout flow for Grails? I'm not entirely sure what goes into working with the PayPal开发者_运维技巧 API, and their documents are quite the mess at the moment with the move to X.com.


Here is the IPNcontroller I revealed from the Paypal plugin to match Paypal IPN requirement. It is in production.

package com.risguru.plugin.ipn

import com.risguru.plugin.shoppingcart.IOrderService

class PaymentController {

IOrderService orderService

def config = grailsApplication.config.grails.paypal

static allowedMethods = [buy: 'POST', notify: 'POST']
static defaultAction = 'index'
def index = {
    redirect(controller:'company', action:'index')
}

def notify = {
    log.debug "Received IPN notification from PayPal Server ${params}"
    try {
        def config = grailsApplication.config.com.risguru.plugin.ipn
        def server = config.server
        def receiver = params.email ?: config.receiver

        if (!server || !receiver) throw new IllegalStateException("Paypal misconfigured! You need to specify the Paypal server URL and/or account email. Refer to documentation.")

        params.cmd = "_notify-validate"
        def queryString = params.toQueryString()[1..-1]

        log.debug "Sending back query $queryString to PayPal server $server"
        def url = new URL(server)
        def conn = url.openConnection()
        conn.doOutput = true
        def writer = new OutputStreamWriter(conn.getOutputStream())
        writer.write queryString
        writer.flush()

        def result = conn.inputStream.text?.trim()

        log.debug "Got response from PayPal IPN $result"

        def purchaseOrder = orderService.getOrderByTransactionID(params.transactionId)

        if (purchaseOrder && result == 'VERIFIED') {
            if (params.receiver_email != receiver) {
                log.warn """WARNING: receiver_email parameter received from PayPal does not match configured e-mail. This request is possibly fraudulent!
                         REQUEST INFO: ${params}
                                        """
            }
            else {
                request.purchaseOrder = purchaseOrder
                def status = params.payment_status
                if (purchaseOrder.paymentStatus != PaymentStatus.COMPLETE && purchaseOrder.paymentStatus != PaymentStatus.CANCELLED) {
                    if (purchaseOrder.paypalTransactionId && purchaseOrder.paypalTransactionId == params.txn_id) {
                        log.warn """WARNING: Request tried to re-use and old PayPal transaction id. This request is possibly fraudulent!
                                REQUEST INFO: ${params} """
                    }
                    else if (status == 'Completed') {
                        purchaseOrder.paypalTransactionId = params.txn_id
                        purchaseOrder.paymentStatus = PaymentStatus.COMPLETE
                        orderService.updateOrderStatus(purchaseOrder)
                        log.info "Verified payment ${purchaseOrder.paypalTransactionId} as COMPLETE"
                    } else if (status == 'Pending') {
                        purchaseOrder.paypalTransactionId = params.txn_id
                        purchaseOrder.paymentStatus = PaymentStatus.PENDING
                        orderService.updateOrderStatus(purchaseOrder)
                        log.info "Verified payment ${purchaseOrder.paypalTransactionId} as PENDING"
                    } else if (status == 'Failed') {
                        purchaseOrder.paypalTransactionId = params.txn_id
                        purchaseOrder.paymentStatus = PaymentStatus.FAILED
                        orderService.updateOrderStatus(purchaseOrder)
                        log.info "Verified payment ${purchaseOrder.paypalTransactionId} as FAILED"
                    }
                }
            }
        }
        else {
            log.error "Error with PayPal IPN response: [$result] and Payment: [${purchaseOrder?.transactionId}]"
        }
    } catch (Exception e) {
        log.error '"**************************************************************************'
        log.error e
        log.error '"**************************************************************************'
    } finally {
        render "OK" // Paypal needs a response, otherwise it will send the notification several times!
    }
}

def success = {
    log.info "Received IPN success from PayPal Server ${params}"
    def uniqueKey = orderService.completeAndGetUniqueKeyByTransactionID(params.transactionId)
    if (!uniqueKey){
        response.sendError 403
        return
    }
    log.info "Purchase Order complete for ${params.transactionId}"
    flash.orderStatus = OrderStatus.COMPLETE
    flash.transactionId = params.transactionId
    flash.uniqueKey = uniqueKey
    if (params.returnAction || params.returnController) {
        def args = [:]
        if (params.returnAction) args.action = params.returnAction
        if (params.returnController) args.controller = params.returnController
        args.params = params
        redirect(args)
    }
    else {
        chain(action:'termOfUse')
    }
}

def cancel = {
    params?.each { key, value ->
        println "[${key}]\t=\t${value}\t::${value?.class?.name}"
    }
    log.info "Cancel Order for ${params.transactionId}"
    def status = orderService.cancelOrder(params.transactionId)
    if (!status){
        response.sendError 403
    }
    flash.orderStatus = OrderStatus.CANCEL
    flash.transactionId = params.transactionId
    if (params.cancelAction || params.cancelController) {
        def args = [:]
        if (params.cancelAction) args.action = params.cancelAction
        if (params.cancelController) args.controller = params.cancelController
        args.params = params
        redirect(args)
    }
    else {
        chain(action:'termOfUse')
    }
}

def termOfUse = {
    if (flash.orderStatus == OrderStatus.COMPLETE || flash.orderStatus == OrderStatus.CANCEL){
        return ['transactionId':flash.transactionId, 'uniqueKey':flash.uniqueKey, message:params.message]
    }

    def transactionId = params.transactionId ?: flash.transactionId
    def uniqueKey = params.uniqueKey ?: flash.uniqueKey

    def orderStatus = orderService.checkOrderStatus(transactionId)
    if (OrderStatus.COMPLETE == orderStatus){
        log.debug "Purchase Order complete for ${transactionId}"
        flash.transactionId = transactionId
        flash.orderStatus = OrderStatus.COMPLETE
        flash.uniqueKey = uniqueKey
    } else {
        def order = orderService.createOrder(uniqueKey)
        switch (order.paymentStatus) {
            case PaymentStatus.FREE:
                flash.transactionId = order.transactionId
                flash.uniqueKey = uniqueKey
                flash.orderStatus = OrderStatus.FREE
                break
            case PaymentStatus.INVALID:
                flash.transactionId = order.transactionId
                flash.uniqueKey = uniqueKey
                flash.orderStatus = OrderStatus.INVALID
                break
            case PaymentStatus.PENDING:
                flash.transactionId = order.transactionId
                flash.uniqueKey = uniqueKey
                flash.orderStatus = OrderStatus.CHARGE
                break
            default:
                response.sendError 403
        }
    }

    return ['transactionId':transactionId, 'uniqueKey':uniqueKey, message:params.message]
}

def buy = {
    if (params.disagree){
        return chain(action:'cancel', params:params)
    }
    def config = grailsApplication.config.com.risguru.plugin.ipn
    def server = config.server
    def receiver = params.email ?: config.receiver
    if (!server || !receiver) throw new IllegalStateException("Paypal misconfigured! You need to specify the Paypal server URL and/or account email. Refer to documentation.")

    def order = orderService.getOrderByTransactionID(params.transactionId)
    if (!order){
        response.sendError 403
    }

    def commonParams = [transactionId: order.transactionId]

    def notifyURL = g.createLink(absolute: true, controller: 'payment', action: 'notify', params: commonParams).encodeAsURL()
    def successURL = g.createLink(absolute: true, controller: 'payment', action: 'success', params: commonParams).encodeAsURL()
    def cancelURL = g.createLink(absolute: true, controller: 'payment', action: 'cancel', params: commonParams).encodeAsURL()

    def url = new StringBuffer("$server?")
    url << "cmd=_xclick&"
    //      url << "business=${config.business}&"
    url << "business=${receiver}&"
    url << "item_name=${order.items[0].itemName}&"
    url << "item_number=${order.items[0].itemNumber}&"
    url << "quantity=${order.items[0].quantity}&"
    url << "amount=${order.items[0].unitPrice}&"
    url << "tax=${order.tax}&"
    url << "currency_code=${order.currency}&"
    if (config.test_ipn) {
        url << "test_ipn=1&"
    }
    if (config.page_style){
        url << "page_style=${config.page_style}&"
    }
    url << "notify_url=${notifyURL}&"
    url << "return=${successURL}&"
    url << "cancel_return=${cancelURL}"

    log.debug "Redirection to PayPal with URL: $url"

    redirect(url: url)
}
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜