## $Id: browser.py 9711 2012-11-23 07:57:57Z henrik $
##
## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
from datetime import datetime
import httplib
import urllib
from xml.dom.minidom import parseString
import grok
from zope.event import notify
from zope.component import getUtility
from waeup.kofa.browser.layout import KofaPage, UtilityView
from waeup.kofa.accesscodes import create_accesscode
from waeup.kofa.interfaces import RETURNING, IKofaUtils
from waeup.kofa.utils.helpers import to_timezone
from waeup.kofa.students.viewlets import ApprovePaymentActionButton as APABStudent
from waeup.kofa.applicants.viewlets import ApprovePaymentActionButton as APABApplicant
from waeup.fceokene.students.interfaces import ICustomStudentOnlinePayment
from waeup.fceokene.applicants.interfaces import ICustomApplicantOnlinePayment
from waeup.fceokene.interfaces import MessageFactory as _

PRODUCT_ID = '83'
SITE_NAME = 'fceokene-kofa.waeup.org'
PROVIDER_ACCT = '0026781725'
PROVIDER_BANK_ID = '31'
PROVIDER_ITEM_NAME = 'BT Education'
INSTITUTION_NAME = 'FCEOkene'
CURRENCY = '566'
#QUERY_URL = 'https://webpay.interswitchng.com/paydirect/services/TransactionQueryURL.aspx'
#QUERY_URL = 'https://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryURL.aspx'
POST_ACTION = 'https://webpay.interswitchng.com/paydirect/webpay/pay.aspx'
#POST_ACTION = 'https://testwebpay.interswitchng.com/test_paydirect/webpay/pay.aspx'

HOST = 'webpay.interswitchng.com'
#HOST = 'testwebpay.interswitchng.com'
URL = '/paydirect/services/TransactionQueryWs.asmx'
#URL = '/test_paydirect/services/TransactionQueryWs.asmx'
httplib.HTTPConnection.debuglevel = 0


def SOAP_post(soap_action,xml):
    """Handles making the SOAP request.

    Further reading:
    http://testwebpay.interswitchng.com/test_paydirect/services/TransactionQueryWs.asmx?op=getTransactionData
    """
    h = httplib.HTTPConnection(HOST)
    headers={
        'Host':HOST,
        'Content-Type':'text/xml; charset=utf-8',
        'Content-Length':len(xml),
        'SOAPAction':'"%s"' % soap_action,
    }
    h.request('POST', URL, body=xml,headers=headers)
    r = h.getresponse()
    d = r.read()
    if r.status!=200:
        raise ValueError('Error connecting: %s, %s' % (r.status, r.reason))
    return d

def get_SOAP_response(product_id, transref):
    xml="""\
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getTransactionData xmlns="http://tempuri.org/">
      <product_id>%s</product_id>
      <trans_ref>%s</trans_ref>
    </getTransactionData>
  </soap:Body>
</soap:Envelope>""" % (product_id, transref)
    result_xml=SOAP_post("http://tempuri.org/getTransactionData",xml)
    doc=parseString(result_xml)
    response=doc.getElementsByTagName('getTransactionDataResult')[0].firstChild.data
    return response

def query_interswitch(payment):
    sr = get_SOAP_response(PRODUCT_ID, payment.p_id)
    wlist = sr.split(':')
    if len(wlist) != 7:
        msg = _('Invalid callback: ${a}', mapping = {'a': sr})
        log = 'invalid callback for payment %s: %s' % (payment.p_id, sr)
        return False, msg, log
    payment.r_code = wlist[0]
    payment.r_desc = wlist[1]
    payment.r_amount_approved = float(wlist[2]) / 100
    payment.r_card_num = wlist[3]
    payment.r_pay_reference = wlist[5]
    payment.r_company = u'interswitch'
    if payment.r_code != '00':
        msg = _('Unsuccessful callback: ${a}', mapping = {'a': sr})
        log = 'unsuccessful callback for %s payment %s: %s' % (
            payment.p_category, payment.p_id, sr)
        payment.p_state = 'failed'
        notify(grok.ObjectModifiedEvent(payment))
        return False, msg, log
    if payment.r_amount_approved != payment.amount_auth:
        msg = _('Callback amount does not match.')
        log = 'wrong callback for %s payment %s: %s' % (
            payment.p_category, payment.p_id, sr)
        payment.p_state = 'failed'
        notify(grok.ObjectModifiedEvent(payment))
        return False, msg, log
    if wlist[4] != payment.p_id:
        msg = _('Callback transaction id does not match.')
        log = 'wrong callback for %s payment %s: %s' % (
            payment.p_category, payment.p_id, sr)
        payment.p_state = 'failed'
        notify(grok.ObjectModifiedEvent(payment))
        return False, msg, log
    payment.p_state = 'paid'
    payment.payment_date = datetime.utcnow()
    msg = _('Successful callback received')
    log = 'valid callback for %s payment %s: %s' % (
        payment.p_category, payment.p_id, sr)
    notify(grok.ObjectModifiedEvent(payment))
    return True, msg, log

class InterswitchActionButtonStudent(APABStudent):
    grok.order(1)
    grok.context(ICustomStudentOnlinePayment)
    grok.require('waeup.payStudent')
    icon = 'actionicon_pay.png'
    text = _('CollegePAY')
    target = 'goto_interswitch'

    @property
    def target_url(self):
        if self.context.p_state != 'unpaid':
            return ''
        return self.view.url(self.view.context, self.target)

class InterswitchActionButtonApplicant(APABApplicant):
    grok.order(1)
    grok.context(ICustomApplicantOnlinePayment)
    grok.require('waeup.payApplicant')
    icon = 'actionicon_pay.png'
    text = _('CollegePAY')
    target = 'goto_interswitch'

    @property
    def target_url(self):
        if self.context.p_state != 'unpaid':
            return ''
        return self.view.url(self.view.context, self.target)

class InterswitchRequestWebserviceActionButtonStudent(APABStudent):
    grok.order(2)
    grok.context(ICustomStudentOnlinePayment)
    grok.require('waeup.payStudent')
    icon = 'actionicon_call.png'
    text = _('Requery CollegePAY')
    target = 'request_webservice'

class InterswitchRequestWebserviceActionButtonApplicant(APABApplicant):
    grok.order(2)
    grok.context(ICustomApplicantOnlinePayment)
    grok.require('waeup.payApplicant')
    icon = 'actionicon_call.png'
    text = _('Requery CollegePAY')
    target = 'request_webservice'

class InterswitchPageStudent(KofaPage):
    """ View which sends a POST request to the Interswitch
    CollegePAY payment gateway.
    """
    grok.context(ICustomStudentOnlinePayment)
    grok.name('goto_interswitch')
    grok.template('student_goto_interswitch')
    grok.require('waeup.payStudent')
    label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
    submit_button = _('Submit')
    action = POST_ACTION
    site_name = SITE_NAME
    currency = CURRENCY
    pay_item_id = ''
    product_id = PRODUCT_ID

    def update(self):
        if self.context.p_state == 'paid':
            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
            self.redirect(self.url(self.context, '@@index'))
            return

        student = self.student = self.context.student
        certificate = getattr(student['studycourse'],'certificate',None)
        self.amount_auth = 100 * self.context.amount_auth
        xmldict = {}
        if certificate is not None:
            xmldict['department'] = certificate.__parent__.__parent__.code
            xmldict['faculty'] = certificate.__parent__.__parent__.__parent__.code
        else:
            xmldict['department'] = None
            xmldict['faculty'] = None
        self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
        tz = getUtility(IKofaUtils).tzinfo
        self.local_date_time = to_timezone(
            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
        self.site_redirect_url = self.url(self.context, 'request_webservice')
        # Provider data
        xmldict['detail_ref'] = self.context.p_id
        xmldict['provider_acct'] = PROVIDER_ACCT
        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
        xmldict['provider_amt'] = 100 * 1600
        # Institution data
        xmldict['fceokene_acct'] = "0000000000000"
        xmldict['institution_bank_id'] = '0'
        xmldict['institution_item_name'] = self.category
        xmldict['institution_name'] = INSTITUTION_NAME
        if self.context.p_category == 'schoolfee':
            self.pay_item_id = '8302'
            studycourse = student['studycourse']
            if student.current_mode in ('ug_sw','prence',):
                xmldict['institution_acct'] = "6216801025"
                xmldict['institution_bank_id'] = '117'
            elif student.current_mode in ('ug_ft',) and \
                student['studycourse'].current_verdict == 'O':
                xmldict['institution_acct'] = "6216801025"
                xmldict['institution_bank_id'] = '117'
            elif student.current_mode in ('ug_ft',):
                xmldict['institution_acct'] = "6216801033"
                xmldict['institution_bank_id'] = '117'
            elif student.current_mode in ('pd_ft',):
                xmldict['institution_acct'] = "6216801025"
                xmldict['institution_bank_id'] = '117'
            xmldict['fceokene_split'] = 100 * 1400
            xmldict['institution_amt'] = 100 * (
                self.context.amount_auth - 1600 - 150 - 1400)
        elif 'maintenance' in self.context.p_category:
            self.pay_item_id = '8300'
            xmldict['institution_amt'] = 100 * (
                self.context.amount_auth - 150)
            xmldict['institution_acct'] = "1012044132"
            xmldict['institution_bank_id'] = '117'
        # Interswitch amount is not part of the xml data

        if self.context.p_category == 'schoolfee':
            xmltext = """<payment_item_detail>
<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
<item_detail item_id="2" item_name="FCEOkene Split" item_amt="%(fceokene_split)s" bank_id="117" acct_num="6216801058" />
<item_detail item_id="3" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
</item_details>
</payment_item_detail>""" % xmldict

        elif 'maintenance' in self.context.p_category:
            xmltext = """<payment_item_detail>
<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
</item_details>
</payment_item_detail>""" % xmldict

        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
        return

class InterswitchPageApplicant(KofaPage):
    """ View which sends a POST request to the Interswitch
    CollegePAY payment gateway.
    """
    grok.context(ICustomApplicantOnlinePayment)
    grok.require('waeup.payApplicant')
    grok.template('applicant_goto_interswitch')
    grok.name('goto_interswitch')
    label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
    submit_button = _('Submit')
    action = POST_ACTION
    site_name = SITE_NAME
    currency = CURRENCY
    pay_item_id = '8303'
    product_id = PRODUCT_ID

    def update(self):
        if self.context.p_state != 'unpaid':
            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
            self.redirect(self.url(self.context, '@@index'))
            return
        if self.context.__parent__.__parent__.expired \
            and self.context.__parent__.__parent__.strict_deadline:
            self.flash(_("Payment ticket can't be send to CollegePAY. "
                         "Application period has expired."))
            self.redirect(self.url(self.context, '@@index'))
            return
        self.applicant = self.context.__parent__
        self.amount_auth = 100 * self.context.amount_auth
        xmldict = {}
        self.category = getUtility(IKofaUtils).PAYMENT_CATEGORIES[self.context.p_category]
        tz = getUtility(IKofaUtils).tzinfo
        self.local_date_time = to_timezone(
            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
        self.site_redirect_url = self.url(self.context, 'request_webservice')
        xmldict['detail_ref'] = self.context.p_id
        # Provider data
        xmldict['provider_amt'] = 100 * 500
        xmldict['provider_acct'] = PROVIDER_ACCT
        xmldict['provider_bank_id'] = PROVIDER_BANK_ID
        xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
        # Institution data
        xmldict['institution_amt'] = 100 * (self.context.amount_auth - 500 - 150)
        xmldict['institution_acct'] = '1012445289'
        xmldict['institution_bank_id'] = '117'
        xmldict['institution_item_name'] = self.context.p_category
        xmldict['institution_name'] = INSTITUTION_NAME
        # Interswitch amount is not part of the xml data
        xmltext = """<payment_item_detail>
<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s">
<item_detail item_id="1" item_name="%(institution_item_name)s" item_amt="%(institution_amt)d" bank_id="%(institution_bank_id)s" acct_num="%(institution_acct)s" />
<item_detail item_id="2" item_name="%(provider_item_name)s" item_amt="%(provider_amt)d" bank_id="%(provider_bank_id)s" acct_num="%(provider_acct)s" />
</item_details>
</payment_item_detail>""" % xmldict
        self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
        return


class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
    """ Request webservice view for the CollegePAY gateway
    """
    grok.context(ICustomStudentOnlinePayment)
    grok.name('request_webservice')
    grok.require('waeup.payStudent')

    def update(self):
        ob_class = self.__implemented__.__name__
        if self.context.p_state == 'paid':
            self.flash(_('This ticket has already been paid.'))
            return
        student = self.context.student
        success, msg, log = query_interswitch(self.context)
        student.writeLogMessage(self, log)
        if not success:
            msg = self.request['QUERY_STRING'].replace('%20',' ')
            self.flash(_('Response from Interswitch: ') + msg)
            return
        success, msg, log = self.context.doAfterStudentPayment()
        if log is not None:
            student.writeLogMessage(self, log)
        self.flash(msg)
        return

    def render(self):
        self.redirect(self.url(self.context, '@@index'))
        return

class InterswitchPaymentRequestWebservicePageApplicant(UtilityView, grok.View):
    """ Request webservice view for the CollegePAY gateway
    """
    grok.context(ICustomApplicantOnlinePayment)
    grok.name('request_webservice')
    grok.require('waeup.payApplicant')

    def update(self):
        if self.context.p_state == 'paid':
            self.flash(_('This ticket has already been paid.'))
            return
        applicant = self.context.__parent__
        success, msg, log = query_interswitch(self.context)
        applicant.writeLogMessage(self, log)
        if not success:
            self.flash(msg)
            return
        success, msg, log = self.context.doAfterApplicantPayment()
        if log is not None:
            applicant.writeLogMessage(self, log)
        self.flash(msg)
        return

    def render(self):
        self.redirect(self.url(self.context, '@@index'))
        return
