Ignore:
Timestamp:
23 Apr 2012, 07:36:46 (13 years ago)
Author:
Henrik Bettermann
Message:

Implement acceptance fee payments via Interswitch. (tests will follow).

Location:
main/waeup.uniben/trunk/src/waeup/uniben
Files:
1 added
3 edited
1 moved

Legend:

Unmodified
Added
Removed
  • main/waeup.uniben/trunk/src/waeup/uniben/etranzact/browser.py

    r8253 r8256  
    3535
    3636def query_etranzact(confirmation_number, user, payment, view):
    37     ob_class = view.__implemented__.__name__.replace('waeup.kofa.','')
     37    ob_class = view.__implemented__.__name__
    3838    postdict = {}
    3939    postdict['TERMINAL_ID'] = TERMINAL_ID
  • main/waeup.uniben/trunk/src/waeup/uniben/interswitch/browser.py

    r8255 r8256  
    2424from waeup.kofa.accesscodes import create_accesscode
    2525from waeup.uniben.students.interfaces import ICustomStudentOnlinePayment
     26from waeup.uniben.applicants.interfaces import ICustomApplicantOnlinePayment
    2627from waeup.kofa.students.browser import write_log_message
    27 from waeup.kofa.students.viewlets import RequestCallbackActionButton
     28from waeup.kofa.students.viewlets import RequestCallbackActionButton as RCABStudent
     29from waeup.kofa.applicants.viewlets import RequestCallbackActionButton as RCABApplicant
    2830from waeup.uniben.students.utils import actions_after_student_payment
     31from waeup.uniben.applicants.utils import actions_after_applicant_payment
    2932from waeup.uniben.interfaces import MessageFactory as _
     33
     34#    Interswitch test account data:
     35#
     36#    Card Number: 6274807700000007
     37#    Expiry Date: July 2012
     38#    PIN: 0000
     39
     40#    Card Number: 6278050000000007
     41#    Expiry Date: July 2012
     42#    PIN: 0000
    3043
    3144PRODUCT_ID = '57'
     
    4558URL = '/test_paydirect/services/TransactionQueryWs.asmx'
    4659httplib.HTTPConnection.debuglevel = 0
     60
     61
     62def collect_xml_data(payment, xmldict):
     63    xmldict['detail_ref'] = payment.p_id
     64    xmldict['provider_amt'] = 100 * payment.surcharge_1
     65    xmldict['provider_acct'] = PROVIDER_ACCT
     66    xmldict['provider_bank_id'] = PROVIDER_BANK_ID
     67    xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
     68    xmldict['institution_amt'] = 100 * payment.amount_auth
     69    xmldict['institution_acct'] = INSTITUTION_ACCT
     70    xmldict['institution_bank_id'] = INSTITUTION_BANK_ID
     71    xmldict['institution_item_name'] = payment.p_category
     72    xmldict['institution_name'] = INSTITUTION_NAME
     73    # Interswitch amount is not part of the xml data
     74    xmltext = """<payment_item_detail>
     75<item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
     76<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" />
     77<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" />
     78</item_details>
     79</payment_item_detail>""" % xmldict
     80    xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
     81    return xml_data
    4782
    4883def SOAP_post(soap_action,xml):
     
    82117    return response
    83118
    84 class InterswitchActionButton(RequestCallbackActionButton):
     119def query_interswitch(user, payment, view):
     120    ob_class = view.__implemented__.__name__
     121    sr = get_SOAP_response(PRODUCT_ID, payment.p_id)
     122    user.loggerInfo(ob_class, 'callback received: %s' % sr)
     123    wlist = sr.split(':')
     124    if len(wlist) != 7:
     125        view.flash(_('Invalid callback: ${a}',
     126            mapping = {'a': wlist}))
     127        user.loggerInfo(ob_class,'invalid callback: %s' % payment.p_id)
     128        return False
     129    payment.r_code = wlist[0]
     130    payment.r_desc = wlist[1]
     131    payment.r_amount_approved = float(wlist[2]) / 100
     132    payment.r_card_num = wlist[3]
     133    payment.r_pay_reference = wlist[5]
     134    if payment.r_code != '00':
     135        view.flash(_('Unsuccessful callback: ${a}', mapping = {'a': wlist[1]}))
     136        user.loggerInfo(ob_class,'unsuccessful callback: %s' % payment.p_id)
     137        payment.p_state = 'failed'
     138        return False
     139    total_amount_auth = (
     140        payment.amount_auth
     141        + payment.surcharge_1
     142        + payment.surcharge_2)
     143    if payment.r_amount_approved != total_amount_auth:
     144        view.flash(_('Wrong amount'))
     145        user.loggerInfo(ob_class,'successful callback but wrong amount: %s'
     146            % payment.p_id)
     147        payment.p_state = 'failed'
     148        return False
     149    if wlist[4] != payment.p_id:
     150        view.flash(_('Wrong transaction id'))
     151        user.loggerInfo(ob_class,'successful callback but wrong transaction id: %s'
     152            % payment.p_id)
     153        payment.p_state = 'failed'
     154        return False
     155    user.loggerInfo(ob_class,'successful callback: %s' % payment.p_id)
     156    payment.p_state = 'paid'
     157    payment.payment_date = datetime.now()
     158    return True
     159
     160
     161class InterswitchActionButtonStudent(RCABStudent):
    85162    grok.order(2)
    86163    grok.context(ICustomStudentOnlinePayment)
     
    95172        return self.view.url(self.view.context, self.target)
    96173
    97 class InterswitchRequestCallbackActionButton(RequestCallbackActionButton):
     174class InterswitchActionButtonApplicant(RCABApplicant):
     175    grok.order(2)
     176    grok.context(ICustomApplicantOnlinePayment)
     177    icon = 'actionicon_pay.png'
     178    text = _('CollegePAY')
     179    target = 'goto_interswitch'
     180
     181    @property
     182    def target_url(self):
     183        if self.context.p_state != 'unpaid':
     184            return ''
     185        return self.view.url(self.view.context, self.target)
     186
     187# Deprecated
     188class InterswitchRequestCallbackActionButtonStudent(RCABStudent):
    98189    grok.order(3)
    99190    grok.context(ICustomStudentOnlinePayment)
     
    112203
    113204# Alternative preferred solution
    114 class InterswitchRequestWebserviceActionButton(RequestCallbackActionButton):
     205class InterswitchRequestWebserviceActionButtonStudent(RCABStudent):
    115206    grok.order(4)
    116207    grok.context(ICustomStudentOnlinePayment)
     
    119210    target = 'request_webservice'
    120211
    121 
    122 class InterswitchPage(KofaPage):
     212class InterswitchRequestWebserviceActionButtonApplicant(RCABApplicant):
     213    grok.order(4)
     214    grok.context(ICustomApplicantOnlinePayment)
     215    icon = 'actionicon_call.png'
     216    text = _('Request CollegePAY webservice')
     217    target = 'request_webservice'
     218
     219
     220class InterswitchPageStudent(KofaPage):
    123221    """ View which sends a POST request to the Interswitch
    124222    CollegePAY payment gateway.
     
    126224    grok.context(ICustomStudentOnlinePayment)
    127225    grok.name('goto_interswitch')
    128     grok.template('goto_interswitch')
     226    grok.template('student_goto_interswitch')
    129227    grok.require('waeup.payStudent')
    130228    label = _('Submit data to CollegePAY (Interswitch Payment Gateway)')
     
    137235
    138236    def update(self):
    139         if self.context.p_state != 'unpaid':
     237        #if self.context.p_state != 'unpaid':
     238        if self.context.p_state == 'paid':
    140239            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
    141240            self.redirect(self.url(self.context, '@@index'))
    142241            return
     242
    143243        self.student = self.context.getStudent()
    144         self.amount = (self.context.amount_auth + self.context.surcharge_1 +
    145             self.context.surcharge_2 + self.context.surcharge_3)
    146         self.amount_100 = 100 * self.amount
    147         self.local_date_time = str(self.context.creation_date)
    148         self.site_redirect_url = self.url(self.context, 'isw_callback')
    149244        certificate = getattr(self.student['studycourse'],'certificate',None)
    150245        xmldict = {}
     
    155250            xmldict['department'] = None
    156251            xmldict['faculty'] = None
    157         xmldict['detail_ref'] = self.context.p_id
    158         xmldict['provider_amt'] = 100 * self.context.surcharge_1
    159         xmldict['provider_acct'] = PROVIDER_ACCT
    160         xmldict['provider_bank_id'] = PROVIDER_BANK_ID
    161         xmldict['provider_item_name'] = PROVIDER_ITEM_NAME
    162         xmldict['institution_amt'] = 100 * self.context.amount_auth
    163         xmldict['institution_acct'] = INSTITUTION_ACCT
    164         xmldict['institution_bank_id'] = INSTITUTION_BANK_ID
    165         xmldict['institution_item_name'] = self.context.p_category
    166         xmldict['institution_name'] = INSTITUTION_NAME
    167         # Interswitch amount is not part of the xml data
    168         xmltext = """<payment_item_detail>
    169 <item_details detail_ref="%(detail_ref)s" college="%(institution_name)s" department="%(department)s" faculty="%(faculty)s">
    170 <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" />
    171 <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" />
    172 </item_details>
    173 </payment_item_detail>""" % xmldict
    174         self.xml_data = """<input type="hidden" name="xml_data" value='%s'  />""" % xmltext
    175         return
    176 
    177 class InterswitchPaymentCallbackPage(UtilityView, grok.View):
     252        self.amount = (self.context.amount_auth + self.context.surcharge_1 +
     253            self.context.surcharge_2 + self.context.surcharge_3)
     254        self.amount_100 = 100 * self.amount
     255        self.local_date_time = str(self.context.creation_date)
     256        self.site_redirect_url = self.url(self.context, 'request_webservice')
     257        self.xml_data = collect_xml_data(self.context, xmldict)
     258        return
     259
     260class InterswitchPageApplicant(InterswitchPageStudent):
     261    """ View which sends a POST request to the Interswitch
     262    CollegePAY payment gateway.
     263    """
     264    grok.context(ICustomApplicantOnlinePayment)
     265    grok.require('waeup.payApplicant')
     266    grok.template('applicant_goto_interswitch')
     267
     268    def update(self):
     269        if self.context.p_state != 'unpaid':
     270            self.flash(_("Payment ticket can't be re-send to CollegePAY."))
     271            self.redirect(self.url(self.context, '@@index'))
     272            return
     273        self.applicant = self.context.__parent__
     274        xmldict = {}
     275        xmldict['department'] = None
     276        xmldict['faculty'] = None
     277        self.amount = (self.context.amount_auth + self.context.surcharge_1 +
     278            self.context.surcharge_2 + self.context.surcharge_3)
     279        self.amount_100 = 100 * self.amount
     280        self.local_date_time = str(self.context.creation_date)
     281        self.site_redirect_url = self.url(self.context, 'request_webservice')
     282        self.xml_data = collect_xml_data(self.context, xmldict)
     283        return
     284
     285# Deprecated
     286class InterswitchPaymentCallbackPageStudent(UtilityView, grok.View):
    178287    """ Callback view for the CollegePAY gateway
    179288    """
     
    237346        self.context.p_state = 'paid'
    238347        self.context.payment_date = datetime.now()
    239         actions_after_payment(student, self.context, self)
     348        actions_after_student_payment(student, self.context, self)
    240349        return
    241350
     
    244353        return
    245354
    246 # Alternative solution, replaces OnlinePaymentCallbackPage
    247 class InterswitchPaymentRequestWebservicePage(UtilityView, grok.View):
     355# Alternative solution, replaces InterswitchPaymentCallbackPage
     356class InterswitchPaymentRequestWebservicePageStudent(UtilityView, grok.View):
    248357    """ Request webservice view for the CollegePAY gateway
    249358    """
     
    257366            return
    258367        student = self.context.getStudent()
    259         sr = get_SOAP_response(PRODUCT_ID, self.context.p_id)
    260         write_log_message(self,'callback received: %s' % sr)
    261         wlist = sr.split(':')
    262         if len(wlist) != 7:
    263             self.flash(_('Invalid callback: ${a}',
    264                 mapping = {'a': wlist}))
    265             write_log_message(self,'invalid callback: %s' % self.context.p_id)
    266             return
    267         self.context.r_code = wlist[0]
    268         self.context.r_desc = wlist[1]
    269         self.context.r_amount_approved = float(wlist[2]) / 100
    270         self.context.r_card_num = wlist[3]
    271         self.context.r_pay_reference = wlist[5]
    272        
    273         if self.context.r_code != '00':
    274             self.flash(_('Unsuccessful callback: ${a}',
    275                 mapping = {'a': wlist[1]}))
    276             write_log_message(
    277                 self,'unsuccessful callback: %s' % self.context.p_id)
    278             self.context.p_state = 'failed'
    279             return
    280 
    281         total_amount_auth = (
    282             self.context.amount_auth
    283             + self.context.surcharge_1
    284             + self.context.surcharge_2)
    285 
    286         if self.context.r_amount_approved != total_amount_auth:
    287             self.flash(_('Wrong amount'))
    288             write_log_message(
    289                 self,'successful callback but wrong amount: %s'
    290                 % self.context.p_id)
    291             self.context.p_state = 'failed'
    292             return
    293 
    294         if wlist[4] != self.context.p_id:
    295             self.flash(_('Wrong transaction id'))
    296             write_log_message(
    297                 self,'successful callback but wrong transaction id: %s'
    298                 % self.context.p_id)
    299             self.context.p_state = 'failed'
    300             return
    301 
    302         write_log_message(self,'successful callback: %s' % self.context.p_id)
    303 
    304         self.context.p_state = 'paid'
    305         self.context.payment_date = datetime.now()
    306 
    307         actions_after_payment(student, self.context, self)
    308 
     368        if query_interswitch(student, self.context, self):
     369            actions_after_student_payment(student, self.context, self)
    309370        return
    310371
     
    312373        self.redirect(self.url(self.context, '@@index'))
    313374        return
     375
     376class InterswitchPaymentRequestWebservicePageApplicant(UtilityView, grok.View):
     377    """ Request webservice view for the CollegePAY gateway
     378    """
     379    grok.context(ICustomApplicantOnlinePayment)
     380    grok.name('request_webservice')
     381    grok.require('waeup.payApplicant')
     382
     383    def update(self):
     384        if self.context.p_state == 'paid':
     385            self.flash(_('This ticket has already been paid.'))
     386            return
     387        applicant = self.context.__parent__
     388        if query_interswitch(applicant, self.context, self):
     389            actions_after_applicant_payment(student, self.context, self)
     390        return
     391
     392    def render(self):
     393        self.redirect(self.url(self.context, '@@index'))
     394        return
  • main/waeup.uniben/trunk/src/waeup/uniben/interswitch/tests.py

    r8020 r8256  
    2323#   If you enable this, please make sure the external services
    2424#   do exist really and are not bothered by being spammed by a test programme.
    25 EXTERNAL_TESTS = False
     25EXTERNAL_TESTS = True
    2626
    2727def external_test(func):
Note: See TracChangeset for help on using the changeset viewer.