def process_completed_payment(self, trans): manager = InvoiceManager() invoice = self.get_invoice_for_transaction(trans) def invoice_logger(msg): raise TrustlyException("Trustly invoice processing failed: {0}".format(msg)) method = trans.paymentmethod pm = method.get_implementation() manager.process_incoming_payment_for_invoice(invoice, trans.amount, 'Trustly id {0}'.format(trans.id), 0, # XXX: we pay zero now, but should perhaps support fees? pm.config('accounting_income'), pm.config('accounting_fee'), [], invoice_logger, method) TrustlyLog(message="Completed payment for Trustly id {0} (order {1}), {2}{3}, invoice {4}".format(trans.id, trans.orderid, settings.CURRENCY_ABBREV, trans.amount, invoice.id), paymentmethod=method).save() send_simple_mail(settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), "Trustly payment completed", "A Trustly payment for {0} of {1}{2} for invoice {3} was completed on the Trustly platform.\n\nInvoice: {4}\nRecipient name: {5}\nRecipient email: {6}\n".format( method.internaldescription, settings.CURRENCY_ABBREV, trans.amount, invoice.id, invoice.title, invoice.recipient_name, invoice.recipient_email), )
def process_stripe_checkout(co): if co.completedat: # Already completed, so don't do anything with it return with transaction.atomic(): method = co.paymentmethod pm = method.get_implementation() api = StripeApi(pm) # Update the status from the API if api.update_checkout_status(co): # Went from unpaid to paid, so Do The Magic (TM) manager = InvoiceManager() invoice = Invoice.objects.get(pk=co.invoiceid) def invoice_logger(msg): raise StripeException( "Stripe invoice processing failed: {0}".format(msg)) manager.process_incoming_payment_for_invoice( invoice, co.amount, 'Stripe checkout id {0}'.format(co.id), co.fee, pm.config('accounting_income'), pm.config('accounting_fee'), [], invoice_logger, method) StripeLog( message= "Completed payment for Stripe id {0} ({1}{2}, invoice {3})". format(co.id, settings.CURRENCY_ABBREV, co.amount, invoice.id), paymentmethod=method).save() send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), "Stripe payment completed", "A Stripe payment for {0} of {1}{2} for invoice {3} was completed.\n\nInvoice: {4}\nRecipient name: {5}\nRecipient email: {6}\n" .format( method.internaldescription, settings.CURRENCY_ABBREV, co.amount, invoice.id, invoice.title, invoice.recipient_name, invoice.recipient_email, ))
def _flag_invoices(request, trans, invoices, pm, fee_account): manager = InvoiceManager() invoicelog = [] transaction.set_autocommit(False) def invoicelogger(msg): invoicelog.append(msg) if len(invoices) == 1: fee = invoices[0].total_amount - trans.amount # Calculated fee else: # There can be no fees when using multiple invoices, so ensure that if sum([i.total_amount for i in invoices]) != trans.amount: raise Exception("Fees not supported for multi-invoice flagging") fee = 0 for invoice in invoices: (status, _invoice, _processor) = manager.process_incoming_payment_for_invoice( invoice, invoice.total_amount, "Bank transfer from {0} with id {1}, manually matched".format( trans.method.internaldescription, trans.methodidentifier), fee, pm.config('bankaccount'), fee_account and fee_account.num, [], invoicelogger, trans.method) if status != manager.RESULT_OK: messages.error(request, "Failed to run invoice processor:") for m in invoicelog: messages.warning(request, m) # Roll back any changes so far transaction.rollback() return False BankTransferFees(invoice=invoice, fee=fee).save() InvoiceLog( message= "Manually matched invoice {0} for {1} {2}, bank transaction {3} {2}, fees {4}" .format( invoice.id, invoice.total_amount, settings.CURRENCY_ABBREV, trans.amount, fee, )).save() # Remove the pending transaction trans.delete() transaction.commit() return True
def process_authorization(notification): method = notification.rawnotification.paymentmethod pm = method.get_implementation() if notification.success: # This is a successful notification, so flag this invoice # as paid. We also create a TransactionStatus for it, so that # can validate that it goes from authorized->captured. trans = TransactionStatus(pspReference=notification.pspReference, notification=notification, authorizedat=datetime.now(), amount=notification.amount, method=notification.paymentMethod, notes=notification.merchantReference, capturedat=None, paymentmethod=method) trans.save() # Generate urls pointing back to this entry in the Adyen online # system, for inclusion in accounting records. urls = [ "https://ca-live.adyen.com/ca/ca/accounts/showTx.shtml?pspReference=%s&txType=Payment&accountKey=MerchantAccount.%s" % (notification.pspReference, notification.merchantAccountCode), ] # We can receive authorizations on non-primary Adyen merchant # accounts. This happens for example with payments from POS # terminals. In those cases, just send an email, and don't # try to match it to any invoices. # We still store and track the transaction. if notification.merchantAccountCode != pm.config('merchantaccount'): send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Manual Adyen payment authorized', "An Adyen payment of %s%s was authorized on the Adyen platform for %s.\nThis payment was not from the automated system, it was manually authorized, probably from a POS terminal.\nReference: %s\nAdyen reference: %s\nMerchant account: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, method.internaldescription, notification.merchantReference, notification.pspReference, notification.merchantAccountCode)) notification.confirmed = True notification.save() # For manual payments, we can only create an open-ended entry # in the accounting accstr = "Manual Adyen payment: %s (%s)" % ( notification.merchantReference, notification.pspReference) accrows = [ (pm.config('accounting_authorized'), accstr, trans.amount, None), ] create_accounting_entry(date.today(), accrows, True, urls) return # Process a payment on the primary account manager = InvoiceManager() try: # Figure out the invoiceid if not notification.merchantReference.startswith( pm.config('merchantref_prefix')): raise AdyenProcessingException( 'Merchant reference does not start with %s' % pm.config('merchantref_prefix')) invoiceid = int( notification. merchantReference[len(pm.config('merchantref_prefix')):]) # Get the actual invoice try: invoice = Invoice.objects.get(pk=invoiceid) except Invoice.DoesNotExist: raise AdyenProcessingException( 'Invoice with id %s does not exist' % invoiceid) def invoice_logger(msg): invoice_logger.invoice_log += msg invoice_logger.invoice_log += "\n" invoice_logger.invoice_log = "" # Handle our special case where an IBAN notification comes in on the creditcard # processor (the primary one), but needs to be flagged as the other one. # If it can't be found, we just flag it on the other method, since the only # thing lost is some statistics. if trans.method == 'bankTransfer_IBAN': # Find our related method, if it exists mlist = list( InvoicePaymentMethod.objects.filter( classname= 'postgresqleu.util.payment.adyen.AdyenBanktransfer'). extra( where=["config->>'merchantaccount' = %s"], params=[pm.config('merchantaccount')], )) if len(mlist) == 1: usedmethod = mlist[0] else: usedmethod = method else: usedmethod = method (status, _invoice, _processor) = manager.process_incoming_payment_for_invoice( invoice, notification.amount, 'Adyen id %s' % notification.pspReference, 0, pm.config('accounting_authorized'), 0, urls, invoice_logger, usedmethod) if status != manager.RESULT_OK: # An error occurred, but nevertheless the money is in our account at this # point. The invoice itself will not have been flagged as paid since something # went wrong, and this also means no full accounting record has been created. # At this point we have no transaction cost, so we just have the payment itself. # Someone will manually have to figure out where to stick it. accrows = [ (pm.config('accounting_authorized'), "Incorrect payment for invoice #{0}".format(invoice.id), notification.amount, None), ] create_accounting_entry(date.today(), accrows, True, urls) send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Error processing invoice from Adyen notification', "An error occured processing the notification for invoice #{0} using {1}.\n\nThe messages given were:\n{2}\n\nAn incomplete accounting record has been created, and the situation needs to be handled manually.\n" .format(invoice.id, method.internaldescription, invoice_logger.invoice_log), ) # Actually flag the notification as handled, so we don't end up repeating it. notification.confirmed = True notification.save() return if invoice.accounting_object: # Store the accounting object so we can properly tag the # fee for it when we process the settlement (since we don't # actually know the fee yet) trans.accounting_object = invoice.accounting_object trans.save() # If nothing went wrong, then this invoice is now fully # flagged as paid in the system. send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Adyen payment authorized', "An Adyen payment of %s%s with reference %s was authorized on the Adyen platform for %s.\nInvoice: %s\nRecipient name: %s\nRecipient user: %s\nPayment method: %s\nAdyen reference: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, notification.merchantReference, method.internaldescription, invoice.title, invoice.recipient_name, invoice.recipient_email, notification.paymentMethod, notification.pspReference)) except AdyenProcessingException as ex: # Generate an email telling us about this exception! send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Exception occured processing Adyen notification', "An exception occurred processing the notification for %s on %s:\n\n%s\n" % (notification.merchantReference, method.internaldescription, ex)) # We have stored the notification already, but we want # to make sure it's not *confirmed*. That way it'll keep # bugging the user. So, return here instead of confirming # it. return else: send_simple_mail( settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Unsuccessful Adyen authorization received', "A credit card authorization for %s on account %s has failed.\nThe reason given was:\n%s\n\nYou don't need to take any further action, nothing has been confirmed in the systems." % ( notification.merchantReference, notification.merchantAccountCode, notification.reason, )) notification.confirmed = True notification.save()
def payment_post(request): nonce = request.POST['payment_method_nonce'] invoice = get_object_or_404(Invoice, pk=get_int_or_error(request.POST, 'invoice'), deleted=False, finalized=True) method = get_object_or_404(InvoicePaymentMethod, pk=get_int_or_error(request.POST, 'method'), active=True) pm = method.get_implementation() if invoice.processor: manager = InvoiceManager() processor = manager.get_invoice_processor(invoice) returnurl = processor.get_return_url(invoice) else: if invoice.recipient_user: returnurl = "%s/invoices/%s/" % (settings.SITEBASE, invoice.pk) else: returnurl = "%s/invoices/%s/%s/" % (settings.SITEBASE, invoice.pk, invoice.recipient_secret) # Generate the transaction result = pm.braintree_sale({ 'amount': '{0}'.format(invoice.total_amount), 'order_id': '#{0}'.format(invoice.pk), 'payment_method_nonce': nonce, 'merchant_account_id': pm.config('merchantacctid'), 'options': { 'submit_for_settlement': True, } }) trans = result.transaction if result.is_success: # Successful transaction. Store it for later processing. At authorization, we proceed to # flag the payment as done. BraintreeLog(transid=trans.id, message='Received successful result for {0}'.format(trans.id), paymentmethod=method).save() if trans.currency_iso_code != settings.CURRENCY_ISO: BraintreeLog(transid=trans.id, error=True, message='Invalid currency {0}, should be {1}'.format(trans.currency_iso_code, settings.CURRENCY_ISO), paymentmethod=method).save() send_simple_mail(settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Invalid currency received in Braintree payment', 'Transaction {0} paid in {1}, should be {2}.'.format(trans.id, trans.currency_iso_code, settings.CURRENCY_ISO)) # We'll just throw the "processing error" page, and have # the operator deal with the complaints as this is a # should-never-happen scenario. return render(request, 'braintreepayment/processing_error.html') with transaction.atomic(): # Flag the invoice as paid manager = InvoiceManager() try: def invoice_logger(msg): raise BraintreeProcessingException('Invoice processing failed: %s'.format(msg)) manager.process_incoming_payment_for_invoice(invoice, trans.amount, 'Braintree id {0}'.format(trans.id), 0, pm.config('accounting_authorized'), 0, [], invoice_logger, method, ) except BraintreeProcessingException as ex: send_simple_mail(settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Exception occurred processing Braintree result', "An exception occured processing the payment result for {0}:\n\n{1}\n".format(trans.id, ex)) return render(request, 'braintreepayment/processing_error.html') # Create a braintree transaction - so we can update it later when the transaction settles bt = BraintreeTransaction(transid=trans.id, authorizedat=timezone.now(), amount=trans.amount, method=trans.credit_card['card_type'], paymentmethod=method) if invoice.accounting_object: bt.accounting_object = invoice.accounting_object bt.save() send_simple_mail(settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'), 'Braintree payment authorized', "A payment of %s%s with reference %s was authorized on the Braintree platform for %s.\nInvoice: %s\nRecipient name: %s\nRecipient user: %s\nBraintree reference: %s\n" % ( settings.CURRENCY_ABBREV, trans.amount, trans.id, method.internaldescription, invoice.title, invoice.recipient_name, invoice.recipient_email, trans.id)) return HttpResponseRedirect(returnurl) else: if not trans: reason = "Internal error" elif trans.status == 'processor_declined': reason = "Processor declined: {0}/{1}".format(trans.processor_response_code, trans.processor_response_text) elif trans.status == 'gateway_rejected': reason = "Gateway rejected: {0}".format(trans.gateway_rejection_reason) else: reason = "unknown" BraintreeLog(transid=trans and trans.id or "UNKNOWN", message='Received FAILED result for {0}'.format(trans and trans.id or "UNKNOWN"), error=True, paymentmethod=method).save() return render(request, 'braintreepayment/payment_failed.html', { 'invoice': invoice, 'reason': reason, 'url': returnurl, })
def process_authorization(notification): if notification.success: # This is a successful notification, so flag this invoice # as paid. We also create a TransactionStatus for it, so that # can validate that it goes from authorized->captured. trans = TransactionStatus(pspReference=notification.pspReference, notification=notification, authorizedat=datetime.now(), amount=notification.amount, method=notification.paymentMethod, notes=notification.merchantReference, capturedat=None) trans.save() # Generate urls pointing back to this entry in the Adyen online # system, for inclusion in accounting records. urls = ["https://ca-live.adyen.com/ca/ca/accounts/showTx.shtml?pspReference=%s&txType=Payment&accountKey=MerchantAccount.%s" % (notification.pspReference, notification.merchantAccountCode),] # We can receive authorizations on non-primary Adyen merchant # accounts. This happens for example with payments from POS # terminals. In those cases, just send an email, and don't # try to match it to any invoices. # We still store and track the transaction. if notification.merchantAccountCode != settings.ADYEN_MERCHANTACCOUNT: send_simple_mail(settings.INVOICE_SENDER_EMAIL, settings.ADYEN_NOTIFICATION_RECEIVER, 'Manual Adyen payment authorized', "An Adyen payment of %s%s was authorized on the Adyen platform.\nThis payment was not from the automated system, it was manually authorized, probably from a POS terminal.\nReference: %s\nAdyen reference: %s\nMerchant account: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, notification.merchantReference, notification.pspReference, notification.merchantAccountCode)) notification.confirmed = True notification.save() # For manual payments, we can only create an open-ended entry # in the accounting accstr = "Manual Adyen payment: %s (%s)" % (notification.merchantReference, notification.pspReference) accrows = [ (settings.ACCOUNTING_ADYEN_AUTHORIZED_ACCOUNT, accstr, trans.amount, None), ] create_accounting_entry(date.today(), accrows, True, urls) return # Process a payment on the primary account manager = InvoiceManager() try: # Figure out the invoiceid if not notification.merchantReference.startswith(settings.ADYEN_MERCHANTREF_PREFIX): raise AdyenProcessingException('Merchant reference does not start with %s' % settings.ADYEN_MERCHANTREF_PREFIX) invoiceid = int(notification.merchantReference[len(settings.ADYEN_MERCHANTREF_PREFIX):]) # Get the actual invoice try: invoice = Invoice.objects.get(pk=invoiceid) except Invoice.DoesNotExist: raise AdyenProcessingException('Invoice with id %s does not exist' % invoiceid) def invoice_logger(msg): raise AdyenProcessingException('Invoice processing failed: %s', msg) manager.process_incoming_payment_for_invoice(invoice, notification.amount, 'Adyen id %s' % notification.pspReference, 0, settings.ACCOUNTING_ADYEN_AUTHORIZED_ACCOUNT, 0, urls, invoice_logger) if invoice.accounting_object: # Store the accounting object so we can properly tag the # fee for it when we process the settlement (since we don't # actually know the fee yet) trans.accounting_object = invoice.accounting_object trans.save() # If nothing went wrong, then this invoice is now fully # flagged as paid in the system. send_simple_mail(settings.INVOICE_SENDER_EMAIL, settings.ADYEN_NOTIFICATION_RECEIVER, 'Adyen payment authorized', "An Adyen payment of %s%s with reference %s was authorized on the Adyen platform.\nInvoice: %s\nRecipient name: %s\nRecipient user: %s\nAdyen reference: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, notification.merchantReference, invoice.title, invoice.recipient_name, invoice.recipient_email, notification.pspReference)) except AdyenProcessingException, ex: # Generate an email telling us about this exception! send_simple_mail(settings.INVOICE_SENDER_EMAIL, settings.ADYEN_NOTIFICATION_RECEIVER, 'Exception occured processing Adyen notification', "An exception occured processing the notification for %s:\n\n%s\n" % ( notification.merchantReference, ex) ) # We have stored the notification already, but we want # to make sure it's not *confirmed*. That way it'll keep # bugging the user. So, return here instead of confirming # it. return