2011-01-26 165 views

回答

2

這是我從貝寶插件透露的匹配PayPal IPN要求的IPN控制器。它正在生產中。

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) 
} 
}