-
Notifications
You must be signed in to change notification settings - Fork 460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Payment.Create not working #105
Comments
Just to confirm, are you trying to create payment using credit card, or vault ? |
The plan is: |
@Zartexx can you try using the sandbox credentials used by the .NET SDK sample project in your project and see if it's able to create the payment successfully? That will help to rule out whether or not it's an issue with your account. Thanks! |
Make sure to create a new Vault Token, as vaults are specific for each merchant (clientId). |
I haven't been saving the vault tokens, If the CC Validation fails, no save occurs. |
Are you authenticating and vaulting using those sample project credentials prior to making the |
"PAC" is the local instance of the internal static class Configuration (copied from sample app) First, when user is on web site we add the CC to the Vault: /// <summary>Add a Credit Card to the Paypal vault and Return the MemberCreditCard record</summary>
/// <param name="member">Member Record</param>
/// <param name="NewCard">New Credit card to Vault then Verify if bad then Delete, good then Insert into our db</param>
/// <param name="CCType">Credit card type. Valid types are: visa, mastercard, discover, amex</param>
/// <param name="CCCVV2">3-4 digit card validation code. Passed thru, never save this!</param>
/// <param name="BillingAddress">Billing address associated with card.</param>
public static MemberCreditCard AddVaultedCreditCardForMember(int MemberId, MemberCreditCard NewCard, int CCCVV2)
{
if (MemberId == 0) { throw new Exception("Member not specified"); }
if (String.IsNullOrWhiteSpace(NewCard.BillingLine1) || String.IsNullOrWhiteSpace(NewCard.BillingPostalCode)) { throw new Exception("Billing Address not specified"); }
MemberCreditCard MembersCard = new MemberCreditCard();
//TODO: Address this when we get clients outside the US: (country_code = "US")
CreditCard card = new CreditCard()
{
billing_address = new Address()
{
city = NewCard.BillingCity,
country_code = "US",
line1 = NewCard.BillingLine1,
postal_code = NewCard.BillingPostalCode,
state = NewCard.BillingState
},
expire_month = NewCard.CardMonth,
expire_year = NewCard.CardYear,
number = NewCard.CardNumber,
type = NewCard.CardType,
cvv2 = CCCVV2.ToString()
};
CreditCard NewCC = card.Create(PAC);
MembersCard.CardMonth = NewCC.expire_month;
MembersCard.CardNumber = NewCC.number; // we will only save the last 4
MembersCard.CardType = NewCC.type;
MembersCard.CardYear = NewCC.expire_year;
MembersCard.CreatedOn = Strings.SafeDate(NewCC.create_time);
MembersCard.IsVerified = (NewCC.state.ToLower() == "ok"); // expired or ok
MembersCard.MemberId = MemberId;
MembersCard.PayPalVaultId = NewCC.id;
MembersCard.UpdatedOn = Strings.SafeDate(NewCC.update_time);
// Cards is now Vaulted
//Lets Verify it
if (ValidateMembersCreditCard(MembersCard) == false)
{
// Throw Error
DeleteVaultedCreditCard(MembersCard);
}
return MembersCard;
}
The Validate method:
private static bool ValidateMembersCreditCard(MemberCreditCard MembersCard)
{
// ____ _ ______ _____ ____ ______ ______
// /\ /\ / __ \ | | |__ __|| __ \ / __ \ |__ __|| ____|
/// / \ || | | || | || | | | || | | | || | |____
//| \ / || |__| || | || | | | || |__| | || | ____|
// \ \/ / | __ || |____ __||__ | |__| || __ | || | |____
// \__/ |_| |_||______||______||_____/ |_| |_| || |______|
decimal AmountToChargeInDollars = 1M;
string ItemName = "Validation";
string FullDescription = "Validating a Credit Card";
Payment pymnt = new Payment()
{
intent = "sale",
payer = new Payer()
{
funding_instruments = new List<FundingInstrument>() { GetFundingInstrument(MembersCard) },
payment_method = "credit_card"
},
transactions = new List<Transaction>() { GetSaleTransaction(MembersCard, AmountToChargeInDollars, ItemName, FullDescription) }
};
bool IsAuthorized = false;
try
{
// Create a payment using a valid APIContext
Payment ZeroChargedPayment = pymnt.Create(PAC);
string authorizationId = ZeroChargedPayment.transactions[0].related_resources[0].authorization.id;
Authorization AuthStatus = Authorization.Get(PAC, authorizationId);
IsAuthorized = (AuthStatus.state == AuthorizationState.Authorized.ToDescription()); // "authorized"
//AuthStatus.Void(PAC); //DRY: PayPal Question: IDK if i'm supposed to do this?
}
catch (PayPal.PaymentsException ex)
{
string errDetails = "";
if (ex.Details != null) { errDetails = ex.Details.debug_id.ToString() + " " + ex.Details.name + ' ' + ex.Details.message + Environment.NewLine + ("" + ex.Details.information_link) + Environment.NewLine; }
if (ex.Details != null && ex.Details.details != null) { ex.Details.details.ForEach(d => errDetails += d.code + " " + d.field + " " + d.issue + Environment.NewLine); }
errDetails += pymnt.ConvertToJson() + Environment.NewLine;
System.Diagnostics.Debug.WriteLine("ERROR: " + ex.Message + Environment.NewLine + errDetails);
throw new Exception("ERROR: " + ex.Message + Environment.NewLine + errDetails,ex);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR: " + ex.Message);
throw;
}
return IsAuthorized;
} // End Function ValidateMembersCreditCard
private static FundingInstrument GetFundingInstrument(MemberCreditCard MembersCard)
{
// A resource representing a credit card that can be used to fund a payment.
FundingInstrument FI = new FundingInstrument() { credit_card_token = GetCreditCardToken(MembersCard) };
return FI;
}
private static CreditCardToken GetCreditCardToken(MemberCreditCard MembersCard)
{
// A resource representing a credit card that can be used to fund a payment.
CreditCardToken JRRToken = new CreditCardToken() { credit_card_id = MembersCard.PayPalVaultId, expire_month = MembersCard.CardMonth, expire_year = MembersCard.CardYear };
return JRRToken;
}
private static Transaction GetSaleTransaction(MemberCreditCard MembersCard, decimal AmountToChargeInDollars, string ItemName, string FullDescription)
{
string strSku = Guid.NewGuid().ToString(); // Just to look official?
string strCost = Strings.FormatNumber(AmountToChargeInDollars, 2).Replace(",", "");
Amount SaleCost = new Amount()
{
currency = "USD",
total = strCost
//,details = new Details() { subtotal = strCost }
};
// Items within a transaction.
var ThingWeSold = new Item() { name = ItemName, price = strCost, currency = "USD", quantity = "1", sku = strSku };
// A transaction defines the contract of a payment - what is the payment for and who is fulfilling it.
Transaction SaleTransaction = new Transaction()
{
amount = SaleCost,
description = FullDescription
//,item_list = new ItemList() { items = new List<Item>() { ThingWeSold } }
};
return SaleTransaction;
} |
Then, later when the event occurs: /// <summary>Be Carefull with this method, as it !!WILL CHARGE A CREDIT CARD!!</summary>
private static Payment ChargeMembersCreditCard(MemberCreditCard MembersCard, decimal AmountToChargeInDollars, string ItemName, string FullDescription)
{
// ____ _ _ ____ _____ ____ ______
// / ___\ | | | | / __ \ | __ \ / ___| | ____|
//| | | |__| || | | || |__| )| | | |____
//| | | __ || |__| || _/ | | ____| ____|
//| |____ | | | || __ || |\ \ | |__| || |____
// \____/ |_| |_||_| |_||_| \_) \____/ |______|
Payment pymnt = new Payment()
{
intent = "sale",
payer = new Payer()
{
funding_instruments = new List<FundingInstrument>() { GetFundingInstrument(MembersCard) },
payment_method = "credit_card"
},
transactions = new List<Transaction>() { GetSaleTransaction(MembersCard, AmountToChargeInDollars, ItemName, FullDescription) }
};
// Create a payment using a valid APIContext
Payment ChargedPayment = pymnt.Create(PAC);
Amount PaymentAmount = ChargedPayment.transactions[0].amount;
Capture PaymentCapture = new Capture() { amount = PaymentAmount, is_final_capture = true };
Authorization PaymentAuthorization = ChargedPayment.transactions[0].related_resources[0].authorization;
Capture ActualPayment = PaymentAuthorization.Capture(PAC, PaymentCapture);
return ChargedPayment;
} // End Function ChargeMembersCreditCard |
Can you share how |
PAC: internal static class Configuration
{
public readonly static string ClientId;
public readonly static string ClientSecret;
private readonly static string PayPalSandBoxCID = "<==redacted==>";
private readonly static string PayPalSandBoxSEC = "<==redacted==>";
private readonly static string PayPalLiveCID = "<==redacted==>";
private readonly static string PayPalLiveSEC = "<==redacted==>";
private readonly static string ConfigSetting_Timeout = "360000";
private readonly static string ConfigSetting_RetryCount = "1";
#region " Implementation "
// Static constructor for setting the readonly static members.
static Configuration()
{
Dictionary<string, string> config = GetConfig(); // Hash Table
ClientId = config[PayPal.Api.BaseConstants.ClientId];
ClientSecret = config[PayPal.Api.BaseConstants.ClientSecret];
}
// Create the configuration map that contains mode and other optional configuration details.
public static Dictionary<string, string> GetConfig()
{
#region " Normally in Web.Config "
/*
<!-- PayPal SDK settings -->
<paypal>
<settings>
<add name="mode" value="sandbox"/>
<add name="connectionTimeout" value="360000"/>
<add name="requestRetries" value="1"/>
<add name="clientId" value="<==redacted==>"/>
<add name="clientSecret" value="<==redacted==>"/>
</settings>
</paypal>
*/
#endregion
Dictionary<string, string> ManualOverRide = new Dictionary<string, string>();
ManualOverRide.Add(PayPal.Api.BaseConstants.HttpConnectionTimeoutConfig, ConfigSetting_Timeout);
ManualOverRide.Add(PayPal.Api.BaseConstants.HttpConnectionRetryConfig, ConfigSetting_RetryCount);
bool ynDebug = true;
if (ynDebug)
{ // DEVELOPMENT
ManualOverRide.Add(PayPal.Api.BaseConstants.ApplicationModeConfig, PayPal.Api.BaseConstants.SandboxMode);
ManualOverRide.Add(PayPal.Api.BaseConstants.EndpointConfig, PayPal.Api.BaseConstants.RESTSandboxEndpoint);
ManualOverRide.Add(PayPal.Api.BaseConstants.ClientId, PayPalSandBoxCID);
ManualOverRide.Add(PayPal.Api.BaseConstants.ClientSecret, PayPalSandBoxSEC);
}
else
{ // LIVE
ManualOverRide.Add(PayPal.Api.BaseConstants.ApplicationModeConfig, PayPal.Api.BaseConstants.LiveMode);
ManualOverRide.Add(PayPal.Api.BaseConstants.EndpointConfig, PayPal.Api.BaseConstants.RESTLiveEndpoint);
ManualOverRide.Add(PayPal.Api.BaseConstants.ClientId, PayPalLiveCID);
ManualOverRide.Add(PayPal.Api.BaseConstants.ClientSecret, PayPalLiveSEC);
}
return ManualOverRide;
//return ConfigManager.Instance.GetProperties();
}
// Create accessToken
private static string GetAccessToken()
{
// ###AccessToken
// Retrieve the access token from OAuthTokenCredential by passing in ClientID and ClientSecret
// It is not mandatory to generate Access Token on a per call basis.
// Typically the access token can be generated once and reused within the expiry window
string accessToken = new OAuthTokenCredential( ClientId, ClientSecret, GetConfig()).GetAccessToken();
return accessToken;
}
// Returns APIContext object
public static APIContext GetAPIContext(string accessToken = "")
{
// ### Api Context
// Pass in a `APIContext` object to authenticate the call and to send a unique request id
// (that ensures idempotency). The SDK generates a request id if you do not pass one explicitly.
APIContext apiContext = new APIContext(string.IsNullOrEmpty(accessToken) ? GetAccessToken() : accessToken);
apiContext.Config = GetConfig();
return apiContext;
}
#endregion
} |
Thanks for sharing your code @Zartexx. I was able to successfully authenticate, vault, and create a payment using your code with the sample sandbox credentials. To do so, I set the following in the private readonly static string PayPalSandBoxCID = "AUASNhD7YM7dc5Wmc5YE9pEsC0o4eVOyYWO9ezXWBu2XTc63d3Au_s9c-v-U";
private readonly static string PayPalSandBoxSEC = "EBq0TRAE-4R9kgCDKzVh09sm1TeNcuY-xJirid7LNtheUh5t5vlOhR0XSHt3"; I am also setting static APIContext PAC = Configuration.GetAPIContext(); Can you confirm if that's what you're doing in your code? Regarding your question from the code about calling |
Yes on the PAC: /// <summary>Optional Override Paypal Api Context</summary>
public static PayPal.Api.APIContext ConnectionOverride;
/// <summary>Paypal Api Context</summary>
private static PayPal.Api.APIContext PAC
{
[System.Diagnostics.DebuggerStepThrough()]
get
{
if (ConnectionOverride == null)
return Configuration.GetAPIContext();
else
return ConnectionOverride;
}
} About the validate, if I change intent to order will I need to void it after? |
I just noticed the ClientId you posted: sample: is different from the one i pulled from PayPal Sample, am I grabbing the wrong id? or are they variable in length? the only success, so far, has been with the 60 character CID/SEC you provided, all of the CID/SEC's I have are 80 characters in length, and none of them are working. (or is this a red herring?) |
The current sample project credentials were created sometime last year when PayPal was generating 60-character credentials for REST apps. I believe current REST apps have 80-character credentials. However, both sets of credentials should work. Do you mind sharing where you're getting the 80-character sample credentials from? I just want to check and make sure there aren't out-of-date credentials floating around somewhere (it could be we deleted the associated REST app when we were cleaning up some of our dev accounts). Also, the flow I should've pointed you towards was the
The sample project includes an example for creating an authorization and then capturing the payment (I'd recommend running the sample project so you can see the steps involved in the flow a bit better, along with what the request & response looks like at each point). The sample is currently setup to use a new credit card, but can be easily updated to use a vaulted card instead: // First vault a credit card.
var card = new CreditCard
{
expire_month = 11,
expire_year = 2018,
number = "4877274905927862",
type = "visa",
cvv2 = "874"
};
var createdCard = card.Create(apiContext);
// Next, create the payment authorization using the vaulted card as the funding instrument.
var payment = new Payment
{
intent = "authorize",
payer = new Payer
{
payment_method = "credit_card",
funding_instruments = new List<FundingInstrument>
{
new FundingInstrument
{
credit_card_token = new CreditCardToken
{
credit_card_id = createdCard.id,
expire_month = createdCard.expire_month,
expire_year = createdCard.expire_year
}
}
}
},
transactions = new List<Transaction>
{
new Transaction
{
amount = new Amount
{
currency = "USD",
total = "1.00"
},
description = "This is the payment transaction description."
}
}
};
var createdPayment = payment.Create(apiContext);
// Get the authorization resource.
var authorization = createdPayment.transactions[0].related_resources[0].authorization;
// Check and make sure the card has been authorized for the payment.
if(authorization.state != "authorized")
{
return;
}
// Specify an amount to capture. By setting 'is_final_capture' to true, all remaining funds held by the authorization will be released from the funding instrument.
var capture = new Capture
{
amount = new Amount
{
currency = "USD",
total = "1.00"
},
is_final_capture = true
};
// Capture the payment.
var responseCapture = authorization.Capture(apiContext, capture); |
Yes, that's the correct page. Something else to try - if you're still getting the 400 error when making the payment, try creating a new REST app and see if the credentials from there work.
By "Event", do you mean the user on your site trying to make a purchase? I was going by the code you've written where it looks like you're trying to verify the card is authorized to make a charge. |
The business model is: someone creates an account, we vault their credit card, then weeks or months later when the business event occurs, they will be charged. They wont be present (on the site) at the time of the charge, which is why we needed direct payment. We only authorized the payment upon vaulting to make sure the credit card is real and valid. |
Your charge method shouldn't need authorization since it was already done when the card was vaulted. All you'll need to do for the charge is do a The one step missing from the code above is to void the authorization (by calling |
Spoke with PayPal today, they fixed it, some sort of back end issue. Thank you guys for all your assistance. |
Glad to hear you got it fixed! 👍 |
I'm hitting the same issue, with basically identical code. I'm saving a card to the vault (client side) and then trying to use the card token on the server side to authorize. I'm not setting the token expire year and month as according to the docs it isn't required but it fails for me even when I do. I guess I'll contact support. |
Hey @jeffgrove ! Please contact merchant support. It seems like few older accounts failed to migrate successfully in one of the migrations. They would be able to reset the configuration to enable you to use your app. |
@jaypatel512 |
I am attempting implementation of the REST API.
but can't get past the line:
Payment ZeroChargedPayment = pymnt.Create(PAC)
all it tells me is UNKNOWN_ERROR
some debug_ids
9916dd16fade6
ebca6a2b07079
I also tried with the Details() in the Amount() and adding the ItemList() to the Transaction()
with the same result.
the json looks like this
the account has direct enable for sandbox
so I don't know what is wrong, I am using the fake credit card from the sample C# REST API
The text was updated successfully, but these errors were encountered: