/
akolonin
/
aigents-java
Обзор
Документация
Войти
/
akolonin
/
aigents-java
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/main/java/net/webstructor/comm/paypal/PayPaler.java
321 строка
15 KB
Anton Kolonin
2.4.4 PayPal integration completed
04 фев 2020, 16:25
04 фев 2020, 16:25
3dfa149
Код
Авторство
О чём код?
/* * MIT License * * Copyright (c) 2019-2020 by Anton Kolonin, Aigents® * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package net.webstructor.comm.paypal; import java.io.IOException; import java.io.StringReader; import java.util.Collection; import java.util.HashSet; import java.util.Map; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; import net.webstructor.agent.Body; import net.webstructor.al.AL; import net.webstructor.al.Parser; import net.webstructor.comm.HTTP; import net.webstructor.comm.HTTPHandler; import net.webstructor.comm.HTTPeer; import net.webstructor.comm.SocialBinder; import net.webstructor.core.Thing; import net.webstructor.peer.Peer; import net.webstructor.peer.Session; import net.webstructor.util.Code; import net.webstructor.util.Str; //Connect with PayPal //https://developer.paypal.com/developer/applications/ //https://developer.paypal.com/docs/connect-with-paypal/#user-experience //https://developer.paypal.com/docs/connect-with-paypal/integrate/ //https://developer.paypal.com/docs/connect-with-paypal/reference/button-js-builder/# //Make Payments //https://developer.paypal.com/docs/archive/checkout/how-to/server-integration/ //https://www.paypal.com/apex/developer/expressCheckout/getAccessToken //List Payments //https://developer.paypal.com/docs/api/get-an-access-token-curl/ //https://developer.paypal.com/docs/api/payments/v1/#payment_list public class PayPaler extends SocialBinder implements HTTPHandler { transient private String access_token = null;//cache access token between the calls public PayPaler(Body body) { super(body,"paypal"); } /** * @param args - array with url, header, request * @return true if handled * @throws IOException */ public boolean handleHTTP(HTTPeer parent, String url, String header, String request, String cookie) throws IOException { if (url.startsWith("/paypal")) try { String client_id = body.self().getString(Body.paypal_id); String client_secret = body.self().getString(Body.paypal_key); if (AL.empty(client_id) || AL.empty(client_secret)) return false; body.debug("PayPal input "+url+" "+request); String paypal_url = body.self().getString(Body.paypal_url,"https://api.paypal.com"); String api_url = paypal_url+"/v1/oauth2/token"; if (url.startsWith("/paypal/create-payment/")) { String type = Str.parseBetween(request, "type=", "&",false); String total = Str.parseBetween(request, "total=", "&",false); String currency = Str.parseBetween(request, "currency=", "&",false); if (AL.empty(total) || AL.empty(currency) || AL.empty(type)) return false; currency = currency.toUpperCase(); net.webstructor.peer.Session session = body.sessioner.getSession(parent == null ? this : parent, cookie);//use parent session type for cookie String title = session.getStoredPeer().getTitle(Peer.title_email); //https://www.paypal.com/apex/developer/expressCheckout/getAccessToken /*String auth_base64 = HTTP.auth_base64(client_id,client_secret); body.debug("PayPal request grant_type=client_credentials "+auth_base64); String response = HTTP.simple(api_url,"grant_type=client_credentials","POST",timeout,null,new String[][] { {"Accept", "application/json"}, {"Accept-language", "en_US"}, {"Authorization",auth_base64}, {"Content-Type", "application/x-www-form-urlencoded"} }); body.debug("PayPal response "+response); JsonReader jsonReader = Json.createReader(new StringReader(response)); JsonObject json = jsonReader.readObject(); access_token = HTTP.getJsonString(json,"access_token");*/ access_token = PayPal.token(body, api_url, timeout, client_id, client_secret); //https://www.paypal.com/apex/developer/expressCheckout/createPayment //https://developer.paypal.com/docs/archive/checkout/how-to/server-integration/#1-set-up-your-client-to-call-your-server String site = body.site(); String name = body.name()+" "+type+" payment: "+title; String params = "{\"intent\": \"sale\",\"note_to_payer\":\""+type+"\",\"payer\": {\"payment_method\": \"paypal\"},\"transactions\": [{\"amount\":{ \"total\": \""+total+"\", \"currency\": \""+currency+"\"},\"description\": \""+type+"\""+ ",\"item_list\":{\"items\":[{" + "\"name\": \""+name+"\"," + "\"sku\": \""+type+"\"," + "\"description\": \""+name+"\"," + "\"price\": \""+total+"\"," + "\"currency\": \""+currency+"\"," + "\"tax\": \"0.0\"," + "\"quantity\": \"1\"" + "}]}" + "}],\"redirect_urls\":{\"return_url\": \""+site+"\",\"cancel_url\": \""+site+"\"}}"; body.debug("PayPal request create "+params); String response = HTTP.simple(paypal_url+"/v1/payments/payment",params,"POST",timeout,null,new String[][] { {"Accept", "application/json"}, {"Accept-language", "en_US"}, {"Authorization","Bearer "+access_token}, {"Content-Type", "application/json"} }); body.debug("PayPal response create "+response); parent.respond(response); return true; } else if (url.startsWith("/paypal/execute-payment/")) { String payment_id = Str.parseBetween(request, "payment=", "&",false); String payer_id = Str.parseBetween(request, "payer=", "&",false); if (AL.empty(payment_id) || AL.empty(payer_id)) return false; net.webstructor.peer.Session session = body.sessioner.getSession(parent == null ? this : parent, cookie);//use parent session type for cookie Thing peer = session.getStoredPeer(); String paypal_id = peer.getString(Body.paypal_id); if (AL.empty(paypal_id)) { Collection existing = body.storager.getByName(Body.paypal_id, payer_id); if (AL.empty(existing)) peer.setString(Body.paypal_id, paypal_id = payer_id);//assign papal id to current peer } if (!paypal_id.equals(payer_id)) { parent.respond("{\"error\": \"PayPal mismatching payer\"}"); return true; } //https://www.paypal.com/apex/developer/expressCheckout/executeApprovedPayment String params = "{\"payer_id\": \""+payer_id+"\"}"; body.debug("PayPal request execute "+payment_id+" "+payer_id); String response = HTTP.simple(paypal_url+"/v1/payments/payment/"+payment_id+"/execute",params,"POST",timeout,null,new String[][] { {"Accept", "application/json"}, {"Accept-language", "en_US"}, {"Authorization","Bearer "+access_token}, {"Content-Type", "application/json"} }); body.debug("PayPal response execute "+response); if (!AL.empty(response)) { //TODO: resync profile //{"id":"PAYID-LYU34MA8AR79209583584949","intent":"sale","state":"approved",...,"create_time":"2020-01-23T15:39:28Z","update_time":"2020-01-23T15:40:10Z", JsonObject json = Json.createReader(new StringReader(response)).readObject(); PayPalItem item = new PayPalItem(json); PayPal paypal = (PayPal)body.getSocializer(name); if (paypal != null && item.valid()) { paypal.updatePeerTerm(item.payer_id, item.date, item.description); body.debug("PayPal updated peer "+item.payer_id+" "+item.date+" "+item.description); //TODO: send message (but do it asyc to avoid HTTP session hangup)!!! } } parent.respond(response); return true; } else if (!AL.empty(request)){ //https://developer.paypal.com/docs/connect-with-paypal/integrate/#6-get-access-token //TODO: get access token and redirect back to original UI Map<String,String> keyvalues = Parser.splitToMap(request, "&","="); String code = keyvalues.get("code"); String scope = keyvalues.get("scope"); if (!AL.empty(code) && !AL.empty(scope)) { //curl -X POST https://api.sandbox.paypal.com/v1/oauth2/token \ // -H 'Authorization: Basic {Your Base64-encoded ClientID:Secret}=' \ // -d 'grant_type=authorization_code&code={authorization_code}' String params = "grant_type=authorization_code&code="+code; String client_id_secret = client_id+":"+client_secret; String auth_base64 = "Basic "+Code.str2b64(client_id_secret,false)+"="; body.debug("PayPal client_id_secret "+client_id_secret); body.debug("PayPal params "+params); body.debug("PayPal auth "+auth_base64); String response = HTTP.simple(api_url,params,"POST",timeout,null,new String[][] { new String[] {"Authorization",auth_base64} }); body.debug("PayPal response "+response); if (!AL.empty(response)) { //https://developer.paypal.com/docs/connect-with-paypal/integrate/#6-get-access-token //{ // "token_type": "Bearer", // "expires_in": "28800", // "refresh_token": {refresh_token}, // "access_token": {access_token} //} JsonReader jsonReader = Json.createReader(new StringReader(response)); JsonObject json = jsonReader.readObject(); String refresh_token = HTTP.getJsonString(json,"refresh_token"); String access_token = HTTP.getJsonString(json,"access_token"); //TODO don't refresh access_token here because it is overkill //curl -X POST https://api.sandbox.paypal.com/v1/oauth2/token \ // -H 'Authorization: Basic {Your Base64-encoded ClientID:Secret}=' \ // -d 'grant_type=refresh_token&refresh_token={refresh token}' params = "grant_type=refresh_token&refresh_token="+refresh_token; body.debug("PayPal request "+params+" "+auth_base64); response = HTTP.simple(api_url,params,"POST",timeout,null,new String[][] {new String[] {"Authorization",auth_base64}}); body.debug("PayPal response "+response); if (!AL.empty(response)) { //https://developer.paypal.com/docs/connect-with-paypal/integrate/#7-exchange-refresh_token-for-access_token //{ // "token_type": "Bearer", // "expires_in": "28800", // "access_token": {access_token} //} jsonReader = Json.createReader(new StringReader(response)); json = jsonReader.readObject(); //TODO sort out why it is not working so far, reporting {"error":"invalid_refresh_token","error_description":"No consent were granted"} if (json.containsKey("access_token")) access_token = HTTP.getJsonString(json,"access_token"); } if (!AL.empty(access_token)) { //https://developer.paypal.com/docs/api/identity/v1/#userinfo_get //curl -v -X GET https://api.sandbox.paypal.com/v1/identity/oauth2/userinfo?schema=paypalv1.1 \ // -H "Content-Type: application/json" \ // -H "Authorization: Bearer Access-Token" api_url = paypal_url+"/v1/identity/oauth2/userinfo?schema=paypalv1.1"; response = HTTP.simple(api_url,null,"GET",timeout,null,new String[][] { new String[] {"Content-Type","application/json"}, new String[] {"Authorization","Bearer "+access_token} }); body.debug("PayPal response "+response); if (!AL.empty(response)) { //TODO: //{ // "user_id": "https://www.paypal.com/webapps/auth/identity/user/mWq6_1sU85v5EG9yHdPxJRrhGHrnMJ-1PQKtX6pcsmA", // "name": "identity test", // "given_name": "identity", // "family_name": "test", // "payer_id": "WDJJHEBZ4X2LY", // "verified_account": "true", // "emails": [ // { // "value": "user1@example.com", // "primary": true // } // ] // } jsonReader = Json.createReader(new StringReader(response)); json = jsonReader.readObject(); String user_id = HTTP.getJsonString(json,"user_id"); String full_name = HTTP.getJsonString(json,"name"); String given_name = HTTP.getJsonString(json,"given_name"); String family_name = HTTP.getJsonString(json,"family_name"); String payer_id = HTTP.getJsonString(json,"payer_id"); String verified_account = HTTP.getJsonString(json,"verified_account"); HashSet<String> emails = new HashSet<String>(); JsonArray emailsJson = json.containsKey("emails") ? json.getJsonArray("emails") : null; if (emailsJson != null) { for (int i = 0; i < emailsJson.size(); i++) { JsonObject emailJson = emailsJson.getJsonObject(i); String email = HTTP.getJsonString(emailJson,"value"); if (!AL.empty(email)) emails.add(email); } } body.debug("PayPal data user_id="+user_id+" full_name="+full_name+" given_name="+given_name+" family_name="+family_name+" payer_id="+payer_id+" verified="+verified_account+" emails="+emails); String paypal_id = !AL.empty(payer_id) ? payer_id : user_id; String[] names = Str.splitName(full_name, given_name, family_name, false); String email = bindUserEmail(name,paypal_id,names[0],names[1],emails); if (!AL.empty(email)) { //TODO update name & surname only if missed!? //TODO if session is authenticated, don't bind it - just set missed attributes? net.webstructor.peer.Session session = body.sessioner.getSession(parent == null ? this : parent, cookie);//use parent session type for cookie session.bind(name, paypal_id, refresh_token, email, names[0], names[1]); if (!AL.empty(session.output())) body.debug("PayPal bind "+session.output()); else body.debug("PayPal bind failed"); } } } } } //TODO: figure out how to delver welcome message to chat view on the Web parent.respond("<html><body>Return to<a href=\""+body.site()+"\">"+body.site()+"</a></body></html>","302 Found\nLocation: "+body.site(),"text/plain"); return true; } } catch (Exception e) { body.error("PayPal error", e); } return false; } protected String getEmail(String id) { return id + "@paypal.com"; } @Override public void output(Session session, String message) throws IOException { //TODO should we disregard PayPaler as a Communicator so no need to have the fake output(...)? } }