I am pinging a docusign account that has over 10k envelopes returned by ListStatusChanges for the 30 days. Note: this is just an initial scan, after that, the time frame shrinks to the last scan + 3 minutes as suggested by DocuSign.
I know only 1 envelope was created by the application ID during that time frame. How can I restrict results to the current application only? I don't want all the envelopes for the account.
public EnvelopesInformation GetEnvelopesInformation(DateTime fromDate)
{
CheckToken();
EnvelopesInformation envelopeInfo = null;
string fromDateStr = fromDate.ToString("o");
EnvelopesApi.ListStatusChangesOptions options = new EnvelopesApi.ListStatusChangesOptions()
{
fromDate = fromDateStr,
fromToStatus = DocusignUtilities.StatusStrings.SIGNED, // excludes delivered, created, and sent envelopes
};
// |EnvelopesApi| contains methods related to envelopes and envelope recipients
EnvelopesApi envelopesApi = new EnvelopesApi(ApiClient.Configuration);
envelopeInfo = envelopesApi.ListStatusChanges(AccountID, options);
return envelopeInfo;
}
One option is your application can use a custom field to identify itself ("myAppMarker").
The code above could be modified like this:
public EnvelopesInformation GetEnvelopesInformation(DateTime fromDate)
{
CheckToken();
EnvelopesInformation envelopeInfo = null;
string fromDateStr = fromDate.ToString("o");
EnvelopesApi.ListStatusChangesOptions options = new EnvelopesApi.ListStatusChangesOptions()
{
customFiled = "myAppMarker",
fromDate = fromDateStr,
fromToStatus = DocusignUtilities.StatusStrings.SIGNED, // excludes delivered, created, and sent envelopes
};
// |EnvelopesApi| contains methods related to envelopes and envelope recipients
EnvelopesApi envelopesApi = new EnvelopesApi(ApiClient.Configuration);
envelopeInfo = envelopesApi.ListStatusChanges(AccountID, options);
return envelopeInfo;
}
Related
I'm trying to implement the SMS authentication with the aid of the DocuSign-SDK library.
var signer = new Signer {...};
signer.RequireIdLookup = "true";
signer.IdCheckConfigurationName = "SMS Auth $";
signer.SmsAuthentication = new RecipientSMSAuthentication {
SenderProvidedNumbers = new List<string> {
"0171*******"
}
};
When I try to send this envelope to the DocuSign API it will reply with the following error message:
Error calling CreateEnvelope:
{"errorCode":"INVALIDAUTHENTICATIONSETUP","message":"Recipient phone
number is invalid. Phone number for SMS Authentication: provided is
invalid. }
INVALIDAUTHENTICATIONSETUP: Authentication is not setup correctly for the recipient.
Is there something I have to enable on the DocuSign Admin page? I couldn't find any feature or something like that I need to enable.
Did I implement it the wrong way? Maybe someone can give me some suggestions.
Thanks
BTW: The given phone number should be valid.
EDIT:
When I'm using the new method as #Inbar wrote, I can't get the needed workflowId from the AccountsApi.
var client = new ApiClient(ApiClient.Demo_REST_BasePath);
var token = "eyJ1...";
client.Configuration.DefaultHeader.Add("Authorization", "Bearer " + token);
var accountsApi = new AccountsApi(client);
var response = accountsApi.GetAccountIdentityVerification(accountId);
var result = response.IdentityVerification; // Is empty. Why?
It seems that I have no IdentityVerification options which I can use for the authentication.
How can I enable such IdentityVerification options?
Or what else do I need to pay attention to?
Your code is using the older method, the new method code is provided in GitHub, I'll post it here too. You can find the article on Dev Center.
string workflowId = phoneAuthWorkflow.WorkflowId;
EnvelopeDefinition env = new EnvelopeDefinition()
{
EnvelopeIdStamping = "true",
EmailSubject = "Please Sign",
EmailBlurb = "Sample text for email body",
Status = "Sent"
};
byte[] buffer = System.IO.File.ReadAllBytes(docPdf);
// Add a document
Document doc1 = new Document()
{
DocumentId = "1",
FileExtension = "pdf",
Name = "Lorem",
DocumentBase64 = Convert.ToBase64String(buffer)
};
// Create your signature tab
env.Documents = new List<Document> { doc1 };
SignHere signHere1 = new SignHere
{
AnchorString = "/sn1/",
AnchorUnits = "pixels",
AnchorXOffset = "10",
AnchorYOffset = "20"
};
// Tabs are set per recipient/signer
Tabs signer1Tabs = new Tabs
{
SignHereTabs = new List<SignHere> { signHere1 }
};
string workflowId = workflowId;
RecipientIdentityVerification workflow = new RecipientIdentityVerification()
{
WorkflowId = workflowId,
InputOptions = new List<RecipientIdentityInputOption> {
new RecipientIdentityInputOption
{
Name = "phone_number_list",
ValueType = "PhoneNumberList",
PhoneNumberList = new List<RecipientIdentityPhoneNumber>
{
new RecipientIdentityPhoneNumber
{
Number = phoneNumber,
CountryCode = countryAreaCode,
}
}
}
}
};
Signer signer1 = new Signer()
{
Name = signerName,
Email = signerEmail,
RoutingOrder = "1",
Status = "Created",
DeliveryMethod = "Email",
RecipientId = "1", //represents your {RECIPIENT_ID},
Tabs = signer1Tabs,
IdentityVerification = workflow,
};
Recipients recipients = new Recipients();
recipients.Signers = new List<Signer> { signer1 };
env.Recipients = recipients;
I've created a new developer account on DocuSign and created a small test app in order to request identity verification options. Fortunately, that was working now and I got all available options but I do not understand why this is not working for my other developer account ("old").
When I compare both accounts I don't see the "Identity Verification" setting in the "old" account.
It is possible to activate this "Identity Verification" setting for my "old" dev account?
I guess that enabling this feature would solve the problem.
EDIT:
Ok, I've solved the problem.
I figured out that no IDV was configured for my developer account. In that case, the identity_verification call will return an empty array.
see: https://developers.docusign.com/docs/esign-rest-api/how-to/id-verification/
Also, I have read the following note in the DocuSign documentation:
Note: Phone authentication may not be enabled for some older developer
accounts. If you cannot choose to use phone authentication for your
account, contact support to request access. see:
https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/recipients/auth/#id-verification-idv
So I contacted DocuSign support and they give me access to the IDV accordingly.
Now it is working fine.
I'm writing a service which allows users to sign up for classes, at the time of sign up the class instructor may not be known, but I need to create a pending charge to assure that the instructor will be paid. Later I want to update the pending and set a finalization date, e.g update the instructors account Id and 24 hours after the class ends pay the instructor
I'm trying to understand the work flow of how to do it, but the API Doc's don't seem to be helpful.
I have a service to create Payment Intents like so:
var paymentIntentService = new PaymentIntentService();
var paymentIntentTransferDataOptions = new PaymentIntentTransferDataOptions();
var options = new PaymentIntentCreateOptions
{
Amount = paymentIntentTransactionOptions.AmountInCents,
Currency = DEFAULT_TRANSACTION_CURRENCY,
ApplicationFeeAmount = this.CalculateApplicationFee(paymentIntentTransactionOptions),
ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
};
var requestOptions = new RequestOptions();
requestOptions.IdempotencyKey = INTENT_IDEM_PREFIX + paymentIntentTransactionOptions.TransactionId;
var paymentIntent = await paymentIntentService.CreateAsync(options, requestOptions);
I would like to create a pending charge with no destination at first and before completion I will update the user to send it to.
The update process I would assume to just, call Get on the PaymentIntent and Update the Sender.
My confusion lays around 3 areas.
How does the API Know what user I'm allowed to send on behalf of a user?
e.g I just provide an accountId. Does the api have full control of other accounts after registration?
How can I Create a pending charge, to assure the instructor gets paid, does that happen when I create the payment intent?
How do I finalize the transaction (Now, or preferable with a future date)
I recently created an entire sharing economy platform with Stripe Connect, so I'm fairly familiar with it.
Charging the User: You'll need to create a regular charge for the user after they schedule the class. Read this on creating charges. This will charge the user $X and hold it in the platforms account without yet transferring it.
Paying the Instructor: You'll need to create a separate transfer using source_transaction to send the money to the instructor's account. source_transaction will basically just make the transfer from the charge you made earlier from the customer. You can read more about that here.
Regarding holding the funds for 24hrs, if you use source_transaction, the money won't become available for payout until after 2 days due to processing. You can read more about that here. If you want it to become available faster, you have 2 options. Either you can enable Instant Payout (which I don't recommend) or you can have a reserve balance in your platform stripe balance to cover the one-day layover and then you can make the transfers without source_transaction.
Okay, so after asking a few more questions and reading api docs. There are a few ways of doing this. #nachshonf answer will get the job done. However if I use transfers and seperate charges, The platform will be responsible for the Stripe fees and refund.
Instead I've come up with a more complex way of doing this, but will assure less of a headache for me in the long run. Basically I create a hold via the platform, and then when the instructor is resolved will request to issue another charge for the instructor. This way all disputes go through the instructor.
First off I'll create a hold via the platform, This is pretty simple
public Task<PaymentIntent>CreatePlatformHoldAsync(long amountInCents, string customerId,
string paymentMethodId, string idem = null, string currency = DEFAULT_TRANSACTION_CURRENCY)
{
var paymentIntentService = new PaymentIntentService();
var options = new PaymentIntentCreateOptions
{
Amount = amountInCents,
Currency = currency,
//ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
CustomerId = customerId,
PaymentMethodId = paymentMethodId,
CaptureMethod = "manual",
};
var requestOptions = new RequestOptions { IdempotencyKey = idem };
return paymentIntentService.CreateAsync(options, requestOptions);
}
/// <summary>
/// Confirm a payment intent, on the platform or sellerAccount
/// </summary>
/// <param name="sellerStripeAccountId">optional, omit for the platform confirm</param>
public Task<PaymentIntent> ConfirmPaymentAsync(string paymentIntentId,
string sellerStripeAccountId = null)
{
var paymentIntentService = new PaymentIntentService();
var paymentIntentConfirmOptions = new PaymentIntentConfirmOptions();
var options = new RequestOptions
{
StripeAccount = sellerStripeAccountId
};
return paymentIntentService.ConfirmAsync(paymentIntentId,
paymentIntentConfirmOptions, options);
}
First create a hold, Then confirm it to authorize the charges. Then I'll create another charge for the instructor
public Task<PaymentIntent> CreateDestinationChargeAsync(long amountInCents, long applicationFeeInCents,
string paymentMethodId, string destinationAccountId, string idem = null,
string currency = DEFAULT_TRANSACTION_CURRENCY)
{
var paymentIntentService = new PaymentIntentService();
var options = new PaymentIntentCreateOptions
{
Amount = amountInCents,
Currency = currency,
ApplicationFeeAmount = applicationFeeInCents,
//ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
PaymentMethodId = paymentMethodId,
};
var requestOptions = new RequestOptions
{
IdempotencyKey = idem,
StripeAccount = destinationAccountId
};
return paymentIntentService.CreateAsync(options, requestOptions);
}
After this is paid you can cancel the platform hold or wait 7 days for the hold to age away.
Edit For Usages
public async Task<Customer> CreateCustomerAsync(string email, string sourceToken)
{
var options = new CustomerCreateOptions
{
Email = email, // "paying.user#example.com",
Source = sourceToken,
};
var service = new CustomerService();
return await service.CreateAsync(options);
}
/// <summary>
/// Creates a payment method for a customer on the sellers stripe account
/// </summary>
/// <returns></returns>
public async Task<PaymentMethod> CreatePaymentMethodAsync(string customerId, string paymentMethodId,
string stripeConnectAccountId)
{
var paymentMethodService = new PaymentMethodService();
var paymentMethodOptions = new PaymentMethodCreateOptions
{
CustomerId = customerId,
PaymentMethodId = paymentMethodId
};
var requestOptions = new RequestOptions()
{
StripeAccount = stripeConnectAccountId
};
return await paymentMethodService.CreateAsync(paymentMethodOptions, requestOptions);
}
Usage:
//set destination here
var destinationAccountId = "";
var configuration = this.GetConfiguration();
StripeConfiguration.ApiKey = configuration["Stripe:ClientSecret"];
//This is the name of the service which I define the methods above
var stripeService = new StripeConnectService(configuration);
//"tok_mastercard" is a test value to represent a paymentToken
var customer = await stripeService.CreateCustomerAsync("CustomerEmail#gmail.com", "tok_mastercard");
var sharedPaymentMethod = await stripeService.CreatePaymentMethodAsync(customer.Id,
customer.DefaultSourceId, destinationAccount.AccountId);
var paymentIntent = await stripeService.CreateDestinationChargeAsync(1000, 100,
sharedPaymentMethod.Id, destinationAccountId);
await stripeService.ConfirmPaymentAsync(paymentIntent.Id, destinationAccountId);
These are examples and are not meant for production I'm only using them to test the flow.
Note: the payment token represents a customer. I haven't implemented a way to obtain the payment token but it looks like you will need stripe.js so they can enter there card information to create a token.
I'm trying to use code I found here to resend an envelope, but no luck. My code is in two parts. Here's the code on my ASPX page to call a method to resend the envelope:
protected void btnResend_Click(object sender, EventArgs e)
{
Signer signer = new Signer();
signer.Email = txtRecipeintEmail.Text;
signer.Name = txtRecipientName.Text;
Manager mgr = new Manager();
mgr.ResendEnvelope(txtEnvelopeID.Text, signer);
}
Here's the code in the Manager class:
public void ResendEnvelope (string envelopeID, Signer signer)
{
// instantiation of recipients as per https://stackoverflow.com/questions/21565765/resend-docusign-emails
Recipients recipients = new Recipients
{
Signers = new List<Signer>()
{
new Signer
{
RecipientId = "1",
RoleName = "Prospect",
Email = signer.Email,
Name = signer.Name,
},
}
};
string accountID = GetAccountID();
EnvelopesApi api = new EnvelopesApi();
EnvelopesApi.UpdateRecipientsOptions options = new EnvelopesApi.UpdateRecipientsOptions();
options.resendEnvelope = "true";
RecipientsUpdateSummary summary = api.UpdateRecipients(accountID, envelopeID, recipients, options);
var responses = summary.RecipientUpdateResults.ToList<RecipientUpdateResponse>();
var errors = responses.Select(rs => rs.ErrorDetails).ToList();
}
My GetAccountID function works fine - I use it to send the envelope. The value in txtEnvelopeID.Text is set from the code used to send the initial email. I get the initial email.
Here's what I see in errors:
?errors[0].Message
"The specified envelope corrections have duplicate recipients."
?errors[0].ErrorCode
"CORRECTION_HAS_DUPLICATE_RECIPIENTS"
When I tried to set the third argument of UpdateRecipients to null, I got a different error. When I left recipients blank (api.UpdateRecipients(accountID, envelopeID, options: = options)), I got an error.
So, I'm out of new ideas to try. Can anyone help?
The issue you are encountering is that you are creating again a signer that already exists, except that you don't assign the same RecipientId, hence the duplicate error.
Instead of
RecipientId = "1"
You need to make sure you assign the original signer ID, see below :
Signers = new List<Signer>()
{
new Signer
{
RecipientId = signer.RecipientId
},
}
In order to re-send the DocuSign email to your recipients, you can use the UpdateRecipient() method as such (see my C# example below). This will re-trigger the signing email to be sent one more time to the transaction recipients you specify in the recipients parameter :
RecipientsUpdateSummary recipientsUpdateSummary =
envelopeApi.UpdateRecipients(
accountId,
EnvelopeId,
RecipientsToNotifyAgain,
new EnvelopesApi.UpdateRecipientsOptions { resendEnvelope = "true" });
Here is what the official documentation states :
I have an account ID and also a template ID, but I am getting null in text custom and list fields. I am using DocuSign's REST api for getting custom fields and listed fields.
configureApiClient("https://demo.docusign.net/restapi");
// Step 1: Login()
// call the Login() API which sets the user's baseUrl and returns their accountId
AccountId = loginApi(username, password);
TemplatesApi envelopesApi2 = new TemplatesApi();
CustomFields cfe = envelopesApi2.ListCustomFields(AccountId, templateId);
Console.WriteLine("Get Custom Fields Information:\n{0}",
JsonConvert.SerializeObject(cfe));
Could you please help me to solve my problem?
Thanks in advance
I see that you are adding the custom fields to an envelope and not template. You should use the EnvelopesApi to retrieve the CustomFields. You are incorrectly using the TemplateId.
Use the following code and pass the envelopeId that is returned from the envelopesApi.CreateEnvelope() call
var envelopesApi2 = new EnvelopesApi();
CustomFields cfe = envelopesApi2.ListCustomFields(AccountId, envelopeId);
Console.WriteLine("Get Custom Fields Information:\n{0}",
JsonConvert.SerializeObject(cfe));
Please see my below code to create custom field in templates.
public EnvelopeSummary requestSignatureFromTemplateTest(DocuSignData data)
{
// instantiate api client with appropriate environment (for production change to www.docusign.net/restapi)
configureApiClient("https://demo.docusign.net/restapi");
//===========================================================
// Step 1: Login()
//===========================================================
// call the Login() API which sets the user's baseUrl and returns their accountId
AccountId = loginApi(username, password);
//===========================================================
// Step 2: Signature Request from Template
//===========================================================
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = "Please sign this sample template document11111111111";
// assign recipient to template role by setting name, email, and role name. Note that the
// template role name must match the placeholder role name saved in your account template.
TemplateRole tRole = new TemplateRole();
tRole.Email = recipientEmail;
tRole.Name = recipientName;
tRole.RoleName = templateRoleName;
List<TemplateRole> rolesList = new List<TemplateRole>() { tRole };
// add the role to the envelope and assign valid templateId from your account
envDef.TemplateRoles = rolesList;
envDef.TemplateId = templateId;
// set envelope status to "sent" to immediately send the signature request
envDef.Status = "sent";
List<TextCustomField> customFieldsTextList = new List<TextCustomField>();
if (data.CustomFieldsText != null)
{
//custom text fields
foreach (DocuSignCustomField customField in data.CustomFieldsText)
{
TextCustomField newField = new TextCustomField();
newField.Name = customField.Name;
newField.Value = customField.Value;
newField.Show = customField.Show;
newField.Required = customField.Required;
customFieldsTextList.Add(newField);
}
}
CustomFields customFields = new CustomFields();
customFields.TextCustomFields = customFieldsTextList;
envDef.CustomFields = customFields;
// |EnvelopesApi| contains methods related to creating and sending Envelopes (aka signature requests)
EnvelopesApi envelopesApi = new EnvelopesApi();
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(AccountId, envDef);
// print the JSON response
//Console.WriteLine("Envelope Template Summary:\n{0}", JsonConvert.SerializeObject(envelopeSummary));
return envelopeSummary;
} // end requestSignatureFromTemplateTest()
this code for getting custom fields from template
configureApiClient("https://demo.docusign.net/restapi");
// Step 1: Login()
// call the Login() API which sets the user's baseUrl and returns their accountId
AccountId = loginApi(username, password);
TemplatesApi envelopesApi2 = new TemplatesApi();
CustomFields cfe = envelopesApi2.ListCustomFields(AccountId, templateId);
Console.WriteLine("Get Custom Fields Information:\n{0}",
JsonConvert.SerializeObject(cfe));
i am integrating express checkout in my asp.net mvc application. everything works ok even, response is success but when i try to call "GetExpressCheckoutDetailsResponseDetails" i am getting null in "PayerID". The field below is "requestDetails.PayerID"
public ActionResult PayPalExpressCheckout()
{
PaymentDetailsType paymentDetail = new PaymentDetailsType();
CurrencyCodeType currency = (CurrencyCodeType)EnumUtils.GetValue("GBP", typeof(CurrencyCodeType));
List<PaymentDetailsItemType> paymentItems = new List<PaymentDetailsItemType>();
var AppCart = GetAppCart();
foreach(var item in AppCart.Items)
{
PaymentDetailsItemType paymentItem = new PaymentDetailsItemType();
paymentItem.Name = item.Name;
paymentItem.Description = item.Description;
double itemAmount = Convert.ToDouble(item.Price);
paymentItem.Amount = new BasicAmountType(CurrencyCodeType.GBP, itemAmount.ToString());
paymentItem.Quantity = 1;
paymentItems.Add(paymentItem);
}
paymentDetail.PaymentDetailsItem = paymentItems;
paymentDetail.PaymentAction = (PaymentActionCodeType)EnumUtils.GetValue("Sale", typeof(PaymentActionCodeType));
paymentDetail.OrderTotal = new BasicAmountType(CurrencyCodeType.GBP, (AppCart.TotalPrice).ToString());
List<PaymentDetailsType> paymentDetails = new List<PaymentDetailsType>();
paymentDetails.Add(paymentDetail);
SetExpressCheckoutRequestDetailsType ecDetails = new SetExpressCheckoutRequestDetailsType();
ecDetails.ReturnURL = "http://Orchard.Club/Purchase/PayPalExpressCheckoutAuthorisedSuccess";
ecDetails.CancelURL = "http://Orchard.Club/Purchase/CancelPayPalTransaction";
ecDetails.PaymentDetails = paymentDetails;
SetExpressCheckoutRequestType request = new SetExpressCheckoutRequestType();
ecDetails.FundingSourceDetails = new FundingSourceDetailsType();
//request.Version = "104.0";
ecDetails.LandingPage = LandingPageType.BILLING;
ecDetails.SolutionType = SolutionTypeType.SOLE;
ecDetails.FundingSourceDetails.UserSelectedFundingSource = UserSelectedFundingSourceType.CREDITCARD;
request.SetExpressCheckoutRequestDetails = ecDetails;
SetExpressCheckoutReq wrapper = new SetExpressCheckoutReq();
wrapper.SetExpressCheckoutRequest = request;
Dictionary<string, string> sdkConfig = new Dictionary<string, string>();
sdkConfig.Add("mode", "sandbox");
sdkConfig.Add("account1.apiUsername", "mrhammad-facilitator_api1.hotmail.com");
sdkConfig.Add("account1.apiPassword", "1369812511");
sdkConfig.Add("account1.apiSignature", "AJxdrs7c7cXRinyNUS5p1V4s1m4uAGR.wOJ7KzgkewEYmTOOtHrPgSxR");
PayPalAPIInterfaceServiceService service = new PayPalAPIInterfaceServiceService(sdkConfig);
SetExpressCheckoutResponseType setECResponse = service.SetExpressCheckout(wrapper);
if (setECResponse.Ack.Equals(AckCodeType.SUCCESS))
{
GetExpressCheckoutDetailsReq getECWrapper = new GetExpressCheckoutDetailsReq();
// (Required) A timestamped token, the value of which was returned by SetExpressCheckout response.
// Character length and limitations: 20 single-byte characters
getECWrapper.GetExpressCheckoutDetailsRequest = new GetExpressCheckoutDetailsRequestType(setECResponse.Token);
// # API call
// Invoke the GetExpressCheckoutDetails method in service wrapper object
GetExpressCheckoutDetailsResponseType getECResponse = service.GetExpressCheckoutDetails(getECWrapper);
// Create request object
DoExpressCheckoutPaymentRequestType expressrequest = new DoExpressCheckoutPaymentRequestType();
DoExpressCheckoutPaymentRequestDetailsType requestDetails = new DoExpressCheckoutPaymentRequestDetailsType();
expressrequest.DoExpressCheckoutPaymentRequestDetails = requestDetails;
requestDetails.PaymentDetails = getECResponse.GetExpressCheckoutDetailsResponseDetails.PaymentDetails;
// (Required) The timestamped token value that was returned in the SetExpressCheckout response and passed in the GetExpressCheckoutDetails request.
requestDetails.Token = setECResponse.Token;
// (Required) Unique PayPal buyer account identification number as returned in the GetExpressCheckoutDetails response
requestDetails.PayerID = requestDetails.PayerID;
// (Required) How you want to obtain payment. It is one of the following values:
// * Authorization – This payment is a basic authorization subject to settlement with PayPal Authorization and Capture.
// * Order – This payment is an order authorization subject to settlement with PayPal Authorization and Capture.
// * Sale – This is a final sale for which you are requesting payment.
// Note: You cannot set this value to Sale in the SetExpressCheckout request and then change this value to Authorization in the DoExpressCheckoutPayment request.
requestDetails.PaymentAction = (PaymentActionCodeType)
Enum.Parse(typeof(PaymentActionCodeType), "SALE");
// Invoke the API
DoExpressCheckoutPaymentReq expresswrapper = new DoExpressCheckoutPaymentReq();
expresswrapper.DoExpressCheckoutPaymentRequest = expressrequest;
// # API call
// Invoke the DoExpressCheckoutPayment method in service wrapper object
DoExpressCheckoutPaymentResponseType doECResponse = service.DoExpressCheckoutPayment(expresswrapper);
// Check for API return status
if (doECResponse.Ack.Equals(AckCodeType.FAILURE) ||
(doECResponse.Errors != null && doECResponse.Errors.Count > 0))
{
return RedirectToAction("PostPaymentFailure");
}
else
{
TempData["TransactionResult"] = "Transaction ID:" + doECResponse.DoExpressCheckoutPaymentResponseDetails.PaymentInfo[0].TransactionID + Environment.NewLine + "Payment status" + doECResponse.DoExpressCheckoutPaymentResponseDetails.PaymentInfo[0].PaymentStatus.Value.ToString();
return RedirectToAction("SaveCustomer", "SignupOrLogin");
}
}
else
{
return RedirectToAction("Error", "Purchase");
}
}
As #geewiz mentioned, you're missing the step of redirecting the customer to PayPal to approve the payment.
Refer to How to Create One-Time Payments Using Express Checkout guide on PayPal Developer that outlines the steps involved with Express Checkout.
In your code, you will want to retrieve the EC token to use for the redirect from the setECResponse object and then redirect the customer to the PayPal site using that token:
SetExpressCheckoutResponseType setECResponse = service.SetExpressCheckout(wrapper);
// Note: Add appropriate logic for targeting live URL based on your config settings
var redirectUrl = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=" + setECResponse.Token;