I'm fiddling with paypalrestsdk on my Flask app and here is the sequence for the payment. Could anyone experienced point out whether the cycle is rigid or not?
Create payment with appropriate redirect URLs.
payment = paypalrestsdk.Payment({
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": url_for('.payment_success', _external=True),
"cancel_url": url_for('.payment_cancel', _external=True)
},
...})
if payment.create():
...
If successful, save the following payment response details in the database.
class PayPalPayment(db.Model):
__tablename__ = 'ediket_paypal_payment'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('ediket_ediketuser.id'))
payment_id = db.Column(db.String(30))
amount = db.Column(db.String(10))
state = db.Column(db.String(10))
redirect_url = db.Column(db.VARCHAR)
created_at = db.Column(db.DateTime)
updated_at = db.Column(db.DateTime)
Then, redirect using HATEOAS links.
Transaction proceeds in PayPal. Redirects to return url.
Find Current User's last transaction with 'created' status from database.
pending_payment = PayPalPayment.query.filter_by(user_id=user_id).filter_by(state='created').first()
Execute Payment using payment_id and payer_id
payment = paypalrestsdk.Payment.find(pending_payment.payment_id)
if payment.execute({"payer_id": request.args.get('PayerID')}):
#We are done!
#Update Payment model for completion
It all works fine, but I'm not too convinced whether this flow is safe for real world application. Since the return url only contains PayerID and Token, the only way to locate the relevant payment to execute is through blind query on Payment table using current user id and status='created'.
Is there a room for improvement?
Any modification since then was that I stored extra field called token, which is passed with HATEOAS links. By storing this token, I was able to locate user's pending payment to proceed with execute.
Update:
Here is the implementation detail. It may not be perfect but it did the trick for now.
class PayPalPayment(db.Model):
__tablename__ = 'paypal_payment'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
payment_id = db.Column(db.String(30))
amount = db.Column(db.String(10))
state = db.Column(db.String(10))
token = db.Column(db.String(30))
redirect_url = db.Column(db.Text)
created_at = db.Column(db.DateTime)
updated_at = db.Column(db.DateTime)
def __init__(self, payment, product_id, user_id):
self.user_id = user_id
self.product_id = product_id
self.payment_id = payment.id
self.amount = payment.transactions[0].amount.total
self.state = payment.state
self.created_at = datetime.datetime.strptime(payment.create_time, "%Y-%m-%dT%H:%M:%SZ")
self.updated_at = datetime.datetime.strptime(payment.update_time, "%Y-%m-%dT%H:%M:%SZ")
for link in payment.links:
if link.method == "REDIRECT":
self.redirect_url = link.href
parsed = urlparse.urlparse(self.redirect_url)
self.token = urlparse.parse_qs(parsed.query)['token'][0]
#payment_view.route('/paypal', methods=['POST'])
#login_required
def paypal_payment():
user_id = current_user.get_id()
product = Product.query.filter_by(id=request.form.get('product')).first_or_404()
past = datetime.datetime.utcnow() - datetime.timedelta(minutes=3)
count = PayPalPayment.query.filter_by(user_id=user_id).filter_by(state='created').filter(PayPalPayment.created_at >= past).count()
# To prevent a spam where user creates Payment and never comes back to it
if count > 4:
return Response(render_template('payment/overflow.html'), mimetype='text/html')
payment = paypalrestsdk.Payment({
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": url_for('.payment_success', _external=True),
"cancel_url": url_for('.payment_cancel', _external=True)
},
"transactions": [{
"amount": {
"total": product.amount,
"currency": "USD"
},
"description": product.description
}]
})
if payment.create():
new_payment = PayPalPayment(payment, product.id, user_id)
db.session.add(new_payment)
db.session.commit()
return redirect(new_payment.redirect_url, code=302)
else:
return Response(render_template('payment/cancel.html'), mimetype='text/html')
#payment_view.route('/success')
#login_required
def payment_success():
user_id = current_user.get_id()
payerID = request.args.get('PayerID', None)
token = request.args.get('token', None)
pending_payment = PayPalPayment.query.filter_by(token=token).filter_by(state='created').first_or_404()
try:
payment = paypalrestsdk.Payment.find(pending_payment.payment_id)
except paypalrestsdk.exceptions.ResourceNotFound:
abort(404)
template = 'payment/error.html'
if payment.execute({"payer_id": request.args.get('PayerID')}):
pending_payment.state = payment.state
pending_payment.updated_at = datetime.datetime.strptime(payment.update_time, "%Y-%m-%dT%H:%M:%SZ")
template = 'payment/success.html'
db.session.commit()
return Response(render_template(template), mimetype='text/html')
Please feel free to criticize.
Related
I'm trying to redirect to view account.view_move_form after invoicing several POS orders and merging those invoices into one, but after the action is done it stays at POS order tree view. Here is my code:
def invoice_and_merge(self):
partner_id = self[0].partner_id
account_moves = []
account_moves_ids = []
wizard = {}
for pedido in self:
if pedido.partner_id != partner_id:
raise UserError('Todas las facturas debes ser del mismo cliente')
#Invoice each order at a time and add to array
account_moves.append(pedido.action_pos_order_invoice())
for move in account_moves:
account_moves_ids.append(move['res_id'])
factura = self.env['account.move'].search([('id', '=', move['res_id'])])
# Set state of created invoices to Draft so they can be merged
factura.button_draft()
wizard['merge_type'] = 'cancel'
wizard['partner_id'] = partner_id.id
merge_invoice = self.env['sh.minv.merge.invoice.wizard'].with_context({'active_ids': account_moves_ids}).create(wizard)
# This is a third party app that merges serveral invoices into a new one and returns a tree view with all new invoices
ret = merge_invoice.action_merge_invoice()
facturas = self.env['account.move'].search([('id','in', ret['domain'][0][2])])
id = 0
for factura in facturas:
if factura.name == '/':
self.account_move = factura
id = factura.id
# Set state of merged invoice to Posted
factura.action_post()
else:
factura.write({'agrupado': True})
# Return form view with the new invoice
return {
'name': _('Customer Invoice'),
'view_mode': 'form',
'view_id': self.env.ref('account.view_move_form').id,
'res_model': 'account.move',
'context': "{'move_type':'out_invoice'}",
'type': 'ir.actions.act_window',
'nodestroy': True,
'res_id': id,
'target': 'current'
}
I have registered an Incoming Messages Webhook in one of my Teams channels.
I am using Microsoft Teams PowerShell to create an adaptive card and send it to a channel:
$SimpleCard = #{
type = "message";
attachments = #(#{
contentType = 'application/vnd.microsoft.card.adaptive';
contentUrl = $null;
content = #{
'$schema' = 'http://adaptivecards.io/schemas/adaptive-card.json';
type = 'AdaptiveCard';
version = '1.2';
body = #(#{
type = 'TextBlock';
text = "Hello <at>andrea.tino#mycompany.com</at>";
})
}
});
msteams = #{
entities = #(#{
type = 'mention';
text = "<at>andrea.tino#mycompany.com</at>";
mentioned = #{
id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
name = 'Andrea Tino';
}
})
}
}
Invoke-RestMethod -Method post
-ContentType 'Application/Json'
-Body ($SimpleCard | ConvertTo-Json -Depth 10)
-Uri "https://myorg.webhook.office.com/webhookb2/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/IncomingWebhook/xxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
The card is correctly sent but the mention is not correctly rendered.
Wrong ID?
It is worth mentioning that the doc makes explicit reference to a type of User ID:
{
"type": "mention",
"text": "<at>John Doe</at>",
"mentioned": {
"id": "29:123124124124",
"name": "John Doe"
}
}
You can see that the ID in the example is: "id": "29:123124124124" while mine is a GUID. I retrieved that User ID by means of Get-TeamUser.
What am I doing wrong?
Mention in adaptive card is supported only in context of bot and the available ways to get id are listed in the document - mention-support-within-adaptive-cards-v12
Concerned team is working to support user mention using AAD Object Id and UPN(use Graph API) in Incoming Webhook.
ID and Name can be fetched from the activity as follows
from: {
id: '29:15fREhoUuf6vVCOuaJYVH-AB6QXXX',
name: 'MOD Administrator',
aadObjectId: 'XXXX'
}
Giving a reference that will help - mention-support-within-adaptive-cards-v12
I am making a website in which users of a website can pay each other on-site like a fiver, Upwork and other platforms.
I want to use the PayPal payment gateway for this purpose. And using Django-rest-framework at the backend.
Are there any tutorials or documentation you guys can refer me to?
here is code when I try to send request to Paypal using payee method.
class PaymentP2P(APIView):
permission_classes = ()
# authentication_classes = (SessionAuthentication, TokenAuthentication)
def post(self,request):
email_request=request.data['payee']
price_to_pay = str(request.data['price'])
payment = paypalrestsdk.Payment(self.build_request_body(email_request,price_to_pay))
print(payment)
if payment.create():
print("Payment created successfully")
else:
print(payment.error)
return Response({'paymentID':payment.id},status=200)
#staticmethod
def build_request_body(email_user="payee#email.com",price="220.00"):
"""Method to create body with a custom PAYEE (receiver)"""
return \
{
"intent": "AUTHORIZE",
"purchase_units": [
{
"amount": {
"total": price,
"currency": "USD"
},
"payee": {
"email_address": "sb-loe4o1374588#personal.example.com"
},
},
]
}
For one user to pay another user's email, you can use PayPal Checkout with a payee variable: https://developer.paypal.com/docs/checkout/integration-features/custom-payee/
Here are some front-end demo patterns of the basic checkout:
One that calls endpoints on your server to set up and capture
transactions:
https://developer.paypal.com/demo/checkout/#/pattern/server
One that
does not do that, and only uses client-side js:
https://developer.paypal.com/demo/checkout/#/pattern/client
I'm trying to execute a refund with PayPal's API in sandbox. I'm getting another internal service error. I am trying to run the following code :
pays.PAYID = _useDoc.client_doc_ref;
//pays.OPERATION = Request["OPERATION"];
if (!string.IsNullOrEmpty(pays.PAYID))
{
RefundRequest refreq = new RefundRequest()
{
//invoice_number = _useDoc.document,
amount = new Amount()
{
currency = "EUR",
total = pays.AMOUNT
}
};
var payment = Payment.Get(apiContext, pays.PAYID);
var transactionId = payment.transactions[0].related_resources[0].sale.id;
var response = Sale.Refund(apiContext, transactionId, refreq);
I am getting the following error:
{
"name": "INTERNAL_SERVICE_ERROR",
"message": "An internal service error occurred.",
"information_link": "https://developer.paypal.com/docs/api/payments/#errors",
"debug_id": "fc27b41e6b6aa"
}
It will be very appreciated if someone helps me in this regard.
I have a form on a page and submit the details of the form on the click of the submit button and want to refresh the page after the object is added to the datastore. I read online that this could be because of the datastore's Eventual Consistency but can't seem to figure out a way to achieve the desired result(refreshing the page).
class AddUpdateProfile(webapp2.RequestHandler):
def post(self):
#This will be used to add/update profile in a datastore. Will be called when the user clicks on submit button on the Profile Page
template = JINJA_ENVIRONMENT.get_template('profile.html')
error = None
name = self.request.get('name')
designation = self.request.get('designation')
salary = self.request.get('salary')
currency = self.request.get('currency')
logging.info("Name = "+name)
logging.info("Designation = "+designation)
logging.info("Salary = "+salary)
logging.info("Currency = "+currency)
profile = UserProfile(parent=userProfile_key(users.get_current_user().email()))
profile.name = name
profile.designation = designation
profile.salary = int(salary)
profile.currency = currency
profile.email = str(users.get_current_user().email())
profile.put()
#Go back to main page. TODO : Change this to update
self.redirect('/profile')
class Profile(webapp2.RequestHandler):
def get(self):
logging.info("Inside Profile Page")
user = users.get_current_user()
if user:
profileInfo = getProfileInformation(user.email())
logging.info("Found a user inside Profile Page")
url = users.create_logout_url(self.request.uri)
if profileInfo is None or not profileInfo:
logging.info("Email = "+user.email())
logging.info("Profile Info not found")
template_values = {
'user': user.nickname(),
'url': url
}
else:
logging.info("Profile Info found")
template_values = {
'user': user.nickname(),
'url': url,
'name' : profileInfo[0].name,
'designation' : profileInfo[0].designation,
'salary' : profileInfo[0].salary,
'currency' : profileInfo[0].currency
}
template_values = template_values
template = JINJA_ENVIRONMENT.get_template('profile.html')
self.response.write(template.render(template_values))
else:
logging.info("User not found. Loading Landing page")
template_values = {
'url' : users.create_login_url(self.request.uri)
}
template = JINJA_ENVIRONMENT.get_template('landing.html')
self.response.write(template.render(template_values))
class MainPage(webapp2.RequestHandler):
def get(self):
logging.info("Inside MainPage")
user = users.get_current_user()
if user:
logging.info("Found a user inside MainPage")
url = users.create_logout_url(self.request.uri)
url_linktext = 'SIGN OUT'
template_values = {
'user': user.nickname(),
'url': url,
'userPage' : "no",
'url_linktext': url_linktext,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
else:
logging.info("User not found. Loading Landing page")
template_values = {
'url' : users.create_login_url(self.request.uri)
}
template = JINJA_ENVIRONMENT.get_template('landing.html')
self.response.write(template.render(template_values))
app = webapp2.WSGIApplication([
('/', MainPage),
('/profile', Profile),
('/addProfile', AddUpdateProfile)
], debug=True)
It would be great if someone could have a look at the code and give me some input on how to resolve the issue.
Any help is really appreciated!
Thanks!
Not sure if this is what you are looking for, but in general, if you want to refresh the page, you should do it using Javascript/JQuery on your page.
Have your endpoint send back a JSON response back to the '/profile'. The response should look something like:
{"success":"success"}
Or if, you need to send an error message:
{"error": "insert error message here"}
Your Javascript and/JQuery should then check if "error" is in the response. If it is throw an error message, otherwise, refresh the page.
How to reload a page using Javascript?