I am using paypal with codeigniter.
I have paypal credentials, find below -
$this->API_USERNAME = '************';
$this->API_PASSWORD = '************';
$this->API_SIGNATURE = '************';
My requirement is: Direct send payment to user's paypal email address. I have only user's paypal email address.
I used Paypal_pro codeigniter library but no luck, any suggestion. FInd my code below -
$nvpStr = "&EMAILSUBJECT=$emailSubject&RECEIVERTYPE=$receiverType&CURRENCYCODE=$currency";
$nvpStr.="&L_EMAIL0=$receiverEmail&L_Amt0=$amount&L_ITEMAMT=$amount&L_UNIQUEID0=$uniqueID&L_NOTE0=$note";
$httpParsedResponseAr = $this->paypal_pro->hash_call('MassPay', $nvpStr);
Response is-
Array ( [TIMESTAMP] => 2013-09-05T17:45:07Z [CORRELATIONID] => 3dfc532f74f43 [ACK] => Failure [VERSION] => 57.0 [BUILD] => 7560199 [L_ERRORCODE0] => 10329 [L_SHORTMESSAGE0] => User is blocked [L_LONGMESSAGE0] => We're sorry, but your PayPal account isn't currently eligible to send mass payments. Please contact either PayPal Customer Service or your account manager. [L_SEVERITYCODE0] => Error )
but above code works fine with test credentials, find below-
$this->API_USERNAME = 'seller_1297608781_biz_api1.lionite.com';
$this->API_PASSWORD = '1297608792';
$this->API_SIGNATURE = 'A3g66.FS3NAf4mkHn3BDQdpo6JD.ACcPc4wMrInvUEqO3Uapovity47p';
Then i changed this line-
$httpParsedResponseAr = $this->paypal_pro->hash_call('SetExpressCheckout', $nvpStr);
But it return below error -
[L_SHORTMESSAGE0] => Transaction refused because of an invalid argument. See additional error messages for details.
[L_LONGMESSAGE0] => Order total is missing.
Can anyone help me whats wrong i did?
MassPay has to be specifically approved and enabled on a live account in order to use it. The sandbox has access to it without approval so that's why it works fine with your test account.
SetExpressCheckout is the first step in integrating the Express Checkout payment flow into your website or application and its NVP string will be entirely different from what you're trying with MassPay. The error you got is because there was no AMT parameter included in the string you sent.
If you're simply trying to send money from your own account to another account you can use the Pay API for that.
I would recommend taking a look at my CodeIgniter library for PayPal which will make this very simple for you to setup your Pay request.
Related
I got issue to post on users profile (Not got error on all users).
Error Code: 400
Error Message: Calls to this method must be made by or on behalf of a Google+ Page.
$options = array( "headers" => array( 'content-type' => 'application/json;' ), "body" => json_encode($activity) );
$httpClient = $this->gplus->client->authorize();
$request = $httpClient->post("googleapis.com/plusPages/v2/people/$Id/activities";, $options);
$response = $request->getBody();
$googlePostResponse = $response->getContents();
$googlePostResponse = json_decode($googlePostResponse, TRUE);
The code you are currently using is to the Google Domains api. The error means that the user you have currently authenticated with doesnt have access to the domains account. Make sure to grant the user access and they will be able to post to the Domains google+ page authentication
Note:
There is a difference between the Google+ API (Socialmedia website) and the Google Domains API (Gsuite)
The google+ api is read only and does not allow for programiticly inserting posts into Google+.
How authentication works
When you authenticate your application using these scopes
$this->client->addScope('googleapis.com/auth/plus.me');
$this->client->addScope('googleapis.com/auth/plus.stream.write');
$this->client->addScope('googleapis.com/auth/plus.stream.read');
$this->client->addScope('googleapis.com/auth/plus.pages.manage');
$this->client->addScope('googleapis.com/auth/plus.media.readwrite');
You are asking the user can I do these things on your behalf. However if the user does not have permission to do something your not going to be able to do it.
calls to this method must be made by or on behalf of a Google+ Page.
The user you are authenticating with does not have access to a business page. So your application cant write to a business page. You can only post to a domain account if you have a gsuite account. If you don't have one then you cant post to it. you cant just give them the id of your business page because again they dont have access to write to your business page.
Magento is periodically logging the following error.
ERR (3): exception 'Exception' with message 'Requested and configured
user#domain.com merchant emails do not match.' in
/home/user/public_html/app/code/core/Mage/Paypal/Model/Ipn.php:257
Based on my reading of the code:
The "configured merchant email" comes from the Magento configuration
The empty "Requested merchant email" comes from the receiver_email
field in the PayPal IPN message.
Why would the IPN message have an empty receiver_email field? I can make several guesses:
Protocol error from PayPal's service.
Configuration error in the PayPal.com account.
Magento bug resulting in empty or corrupt data.
Network problem resulting in empty or corrupt data.
Which is more likely, and how can I narrow it down?
It appears to be related to an adjustment transaction. A few odd things:
The referenced order was paid by credit card, not Paypal Express.
The referenced order was adjusted months ago.
This latest transaction is missing from the order comment history.
I suspect this is a code or protocol error. The transaction POST data is below with identifiers obscured to protect the innocent.
Array
(
[txn_type] => adjustment
[payment_date] => 12:17:03 Jul 20, 2015 PDT
[payment_gross] => -64.45
[mc_currency] => USD
[verify_sign] => ...
[payer_status] => verified
[payer_email] => seller#address.com
[txn_id] => ...
[parent_txn_id] => ...
[payer_id] => ...
[invoice] => 123456789
[reason_code] => chargeback_settlement
[payment_status] => Completed
[payment_fee] => -20.00
[mc_gross] => -64.45
[charset] => windows-1252
[notify_version] => 3.8
[ipn_track_id] => ...
)
In my case the issue was the configured email was not the primary email set on my paypal account. So, paypal was sending the default email account (which was different) as the requested email. After setting the configured email as the primary email address in paypal the issue seems to have been fixed.
I think this is a Magento bug. For chargebacks the merchant is actually the "payer". That is why in this case the address can be found in the payer_email field. The _verifyOrder method will have to be adjusted accordingly.
In my case the error happened after changing the primary email in the PayPal admin panel.
When changing the primary email in PayPal you need to generate new API credentials since it looks like the credential are based on the primary email at the moment of its generation.
Once you remove the old API credentials and generate new ones, update the credentials in Magento (Stores > Configuration > Sales > Payment Methods > PayPal Express Checkout), flush the cache and the problem should be gone.
I am writing a refund function for our ecommerce site and I'm getting error code 3033 : The RelatedSecurityKey is required.
I have checked and the documentation says this key should be returned when the original transaction was made. We save the details without any modifications to the database, but I can't find it. The return values for my test transaction are:
VendorTxCode=241****
&VPSTxId={EBA625AB-955E-75C8-4409-*********}
&Status=OK
&StatusDetail=0000 : The Authorisation was Successful.
&TxAuthNo=763****
&AVSCV2=SECURITY CODE MATCH ONLY
&AddressResult=NOTMATCHED
&PostCodeResult=NOTMATCHED
&CV2Result=MATCHED
&GiftAid=0
&3DSecureStatus=NOTCHECKED
&CardType=VISA
&Last4Digits=0006
&Amount=25.00
Any suggestions?
SecurityKey - Only present if Status is OK.
To register the initial transaction, you post to Sage Pay via the Transaction Registration URL (A1).
Sage Pay send a server reponse to the tranasction registration POST in the Server protocol (see A2 in Sage Pay Server Protocol). The response includes the following - VPSProtocol, Status, StatusDetail, VPSTxId, SecurityKey and NextURL.
[A Security key which Sage Pay uses to generate a MD5 Hash to sign the Notification message (step A3 from the Sage Pay Server protocol, which is the example you are providing in response to the Notification post via the Notification URL). The signature is called VPSSignature. This value is used to allow detection of tampering with notifications from the Sage Pay Server. It must be kept secret from the Customer and held in your database. Only present if Status is OK.] You need the SecurityKey incase want to refund against the transaction in future as this will become the RelatedSecurityKey.
The NextURL redirects the customer/shopper to the Sage Pay payment page.
Sage Pay then notify you via the NotificationURL with the VPSSignature. (A3)
You then acknowledge receipt of the post and the customer is redirected via the RedirectURL back to a page on your website.
The Stripe API reference says this about authentication:
The example they give is this:
require "stripe"
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
The sk_test_BQokikJOvBiI2HlWgH4olfQ2 secret key is found in the account settings on Stripe's webpage. I understand this is the secret api key for my application to talk with Stripe.
But then I read this documentation on getting started with Stripe Connect:
When using our official API libraries, we recommend that you pass in the
access_token with every request, instead of setting the API key globally.
This is because the access_token used in any API request depends on the user
you're charging on behalf of.
The example they give is:
# Not recommended: setting global API key state
Stripe.api_key = ACCESS_TOKEN
Stripe::Customer.create(
:description => "example#stripe.com"
)
# Recommended: sending API key with every request
Stripe::Customer.create(
{:description => "example#stripe.com"},
ACCESS_TOKEN # user's access token from the Stripe Connect flow
)
Here, the access token is returned to the application after a user has connected to the application through Stripe Connect. The access token can be used to perform actions on behalf of that user, like charging their card.
So, they pass the API key with every request, but why would the user's access token be an api key? I thought from the first documentation that the api key is supposed to be my application's secret api key? Instead, they are setting the user's access token. How will Stripe identify my application then if I'm setting the user's access token and not my own secret key?
Then, I read their example on integrating Stripe Checkout with Sinatra. The code sample they give is:
require 'sinatra'
require 'stripe'
set :publishable_key, ENV['PUBLISHABLE_KEY']
set :secret_key, ENV['SECRET_KEY']
Stripe.api_key = settings.secret_key
....
get '/' do
erb :index
end
post '/charge' do
# Amount in cents
#amount = 500
customer = Stripe::Customer.create(
:email => 'customer#example.com',
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:amount => #amount,
:description => 'Sinatra Charge',
:currency => 'usd',
:customer => customer.id
)
erb :charge
end
So in this instance, they set the API Key to be the application's secret key. They don't pass any Access Token in the request either. So I'm a bit confused why an Access Token would be set as a secret API Key in the previous doc or why I should pass it with each request, when all their example docs don't even do that.
To understand this, you should know first that the Stripe API can be used to build applications that serve two kinds of audiences:
to accept payments from end-users as a merchant (normal use-case) and
to provide add-on services to merchants having their own Stripe
accounts (eg. one service helps me configure the emails to be sent out on different Stripe events)
Hence, all the API endpoints can be authorized in two ways:
the API key way which you can directly get from your Account Settings. This identifies your Stripe account
the access token way through Stripe Connect. This identifies the Stripe account of the connected merchant.
What the Stripe Connect docs is telling you is that suppose you are building an application that serves use-case #2 above, then you must remember to authorize each of your API calls with the right access token and not have a global API key (which, by the way, is fully acceptable for use case #1) as you might be making changes incorrectly to the wrong account(s).
So, if use case #1 is what you want to do, you don't have to worry about Stripe Connect at all.
I've carefully reviewed Steve Bazyl's presentation at https://www.youtube.com/watch?v=iK14bfd6qhs and relevant API docs on google. I'm using the Service Account email id for my gmail account, and am using the private key downloaded from the Console.
But when I run the test client modeled after the one Steve showed in his presentation I consistently get
Signet::AuthorizationError:
Authorization failed. Server message:
{
"error" : "invalid_grant"
}
I get the same error message if I add garbage letters to the email or scope passed to JWTAsserter. Clearly something wrong but I can't seem to figure out how to troubleshoot this.
Here's the client code I'm running (in a rails rspec file):
client = Google::APIClient.new
key_file = '/Users/stu/projects/br/rails-app/######-privatekey.p12'
key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, 'notasecret')
Rails.logger.info "Private key? #{key.private?}"
asserter = Google::APIClient::JWTAsserter.new(
'#####-#######knp#developer.gserviceaccount.com',
"https://www.googleapis.com/auth/calendar",
key)
client.authorization = asserter.authorize()
I'm pretty well stuck, would definitely appreciate any troubleshooting advice.
Thanks!
Update
Thanks for sharing code that works for you Jack.
I've gone to my site's dev console and created a service account client p12 key. I then went to the site's Admin Console and added my client id granting site-wide authorization to the calendar API
In the Admin Console after adding the authorization it looks like this:
XXXXXXXXXXXhnq.apps.googleusercontent.com Calendar (Read-Write) https://www.googleapis.com/auth/calendar
I downloaded the p12 key and used it in the code structure you provided. I also tried with the approach from Steve Bazyl's presentation:
asserter = Google::APIClient::JWTAsserter.new(
"XXXXXXXXXXX-hnq#developer.gserviceaccount.com",
"https://www.googleapis.com/auth/calendar",
key)
client.authorization = asserter.authorize("stu#XXXXXXXXXX.com")
In both cases I get the same output as before:
Signet::AuthorizationError:
Authorization failed. Server message:
{
"error" : "invalid_grant"
}
I get that same output if I type in junk instead of "XXXXs://www.googleapis.com/auth/calendar". The key is valid, and while it's clear I'm doing something wrong, I can't find any clues in the API or google about how to tell what it is.
Any ideas how to troubleshoot?
Have you given service account access to your Google Apps account? You can find out how to that here: https://developers.google.com/+/domains/authentication/delegation#delegate_domain-wide_authority_to_your_service_account
Following code works for me:
key = Google::APIClient::KeyUtils.load_from_pkcs12('tmp/##########-privatekey.p12', 'notasecret')
client = Google::APIClient.new({:application_name => "example-app", :application_version => "1.0"})
client.authorization = Signet::OAuth2::Client.new(
:person => 'name#example.com',
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/drive.readonly',
:issuer => '123456789#developer.gserviceaccount.com',
:signing_key => key)
client.authorization.fetch_access_token!
drive = client.discovered_api('drive', 'v2')
result = client.execute(api_method: drive.files.list)
There is a long thread regarding this on GitHub which seems to indicate the problem is related to the system's date and time being "a little off." The suggested solution is to run sudo ntpdate ntp.ubuntu.com.
The thread is closed but it does have a link to this information from Google about "Invalid Grant".
Never was able to figure out how to get past the invalid_grant error.
My solution was to get a refresh token using the normal webapp api. Once retrieved it seems to be usable indefinitely so the end result appears to be the same.