|
Hello Guys,
I have create a Android application in Xamarin and i would like to also have in on Amazon store.
It uses in App purchases
Currently im implementing those purchases with this code:
public async Task<bool> WasItemPurchased(string productId)
{
var billing = CrossInAppBilling.Current;
var result = false;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
if (!connected)
{
return false;
}
var verify = new InAppBillingVerify();
var purchases = await billing.GetPurchasesAsync(ItemType.InAppPurchase, verify);
if (purchases?.Any(p => p.ProductId == productId) ?? false)
{
Preferences.Set(productId, true);
return true;
}
else
{
return false;
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
var message = string.Empty;
switch (purchaseEx.PurchaseError)
{
case PurchaseError.AppStoreUnavailable:
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorAppStoreUnavailable);
break;
case PurchaseError.BillingUnavailable:
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorBillingUnavailable);
break;
case PurchaseError.PaymentInvalid:
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentInvalid);
break;
case PurchaseError.PaymentNotAllowed:
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentNotAllowed);
break;
case PurchaseError.AlreadyOwned:
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorAlreadyOwned);
Preferences.Set(productId, true);
result = true;
break;
}
if (string.IsNullOrWhiteSpace(message))
return false;
}
catch (Exception exception)
{
Crashes.TrackError(exception);
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_ErrorAppStoreUnavailable);
}
finally
{
await billing.DisconnectAsync();
}
return result;
}
public async Task<bool> PurchaseItem(string productId, string payload)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
if (!connected)
{
return false;
}
var verify = new InAppBillingVerify();
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload, verify);
if (purchase == null)
{
}
else if (purchase.State == PurchaseState.Purchased)
{
Preferences.Set(productId, true);
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
var message = string.Empty;
switch (purchaseEx.PurchaseError)
{
case PurchaseError.AppStoreUnavailable:
message = GetString(Resource.String.Options_PaidFunctions_ErrorAppStoreUnavailable);
break;
case PurchaseError.BillingUnavailable:
message = GetString(Resource.String.Options_PaidFunctions_ErrorBillingUnavailable);
break;
case PurchaseError.PaymentInvalid:
message = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentInvalid);
break;
case PurchaseError.PaymentNotAllowed:
message = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentNotAllowed);
break;
case PurchaseError.AlreadyOwned:
message = GetString(Resource.String.Options_PaidFunctions_ErrorAlreadyOwned);
Preferences.Set(productId, true);
EnableOrDisableProFeatures();
break;
}
if (string.IsNullOrWhiteSpace(message))
return false;
DisplayToast(message, ToastLength.Long);
}
catch (Exception exception)
{
Crashes.TrackError(exception);
DisplayToast(GetString(Resource.String.Options_PaidFunctions_ErrorAppStoreUnavailable), ToastLength.Long);
}
finally
{
await billing.DisconnectAsync();
}
return false;
}
private async Task RestorePurchases()
{
var currentConnectivity = Connectivity.NetworkAccess;
var restorePurchasesResults = new List<bool>();
if (currentConnectivity == NetworkAccess.Internet)
{
restorePurchasesResults.Add(await WasItemPurchased(GetString(Resource.String.Options_PaidFunctions_PurchaseTemplatesAndMoreButtons)));
restorePurchasesResults.Add(await WasItemPurchased(GetString(Resource.String.Options_PaidFunctions_PurchaseCustomResources)));
restorePurchasesResults.Add(await WasItemPurchased(GetString(Resource.String.Options_PaidFunctions_PurchaseDisplayWebpageAfterVote)));
if (restorePurchasesResults.Contains(true))
{
DisplayToast(GetString(Resource.String.Options_PaidFunctions_PurchasesRestored), ToastLength.Long);
EnableOrDisableProFeatures();
}
else
{
if (string.IsNullOrEmpty(RestorePurchasesStatusMessage))
RestorePurchasesStatusMessage = GetString(Resource.String.Options_PaidFunctions_PurchasesNotFound);
DisplayToast(RestorePurchasesStatusMessage, ToastLength.Long);
}
}
else
{
DisplayToast(GetString(Resource.String.Options_InternetUnavailable), ToastLength.Long);
}
}
private async Task<string> GetProductPrice(string productId)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
if (!connected)
{
return string.Empty;
}
var item = await billing.GetProductInfoAsync(ItemType.InAppPurchase, productId);
if (item != null)
return item.ToList()[0].LocalizedPrice;
}
catch (InAppBillingPurchaseException purchaseEx)
{
var message = string.Empty;
switch (purchaseEx.PurchaseError)
{
case PurchaseError.AppStoreUnavailable:
message = GetString(Resource.String.Options_PaidFunctions_ErrorAppStoreUnavailable);
break;
case PurchaseError.BillingUnavailable:
message = GetString(Resource.String.Options_PaidFunctions_ErrorBillingUnavailable);
break;
case PurchaseError.PaymentInvalid:
message = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentInvalid);
break;
case PurchaseError.PaymentNotAllowed:
message = GetString(Resource.String.Options_PaidFunctions_ErrorPaymentNotAllowed);
break;
}
if (string.IsNullOrWhiteSpace(message))
return string.Empty;
DisplayToast(message, ToastLength.Long);
}
catch (Exception exception)
{
Crashes.TrackError(exception);
}
finally
{
await billing.DisconnectAsync();
}
return string.Empty;
}
And i have to do similar function for the Amazon store like the API details here:
https://developer.amazon.com/docs/cross-platform-plugins/cpp-use-the-iap-plugin-for-xamarin.html
However i dont even know where to start to convert those functions, Amazon API confuse me, can someone help me?
|
|
|
|
|
We are more than willing to help those that are stuck: but that doesn't mean that we are here to do it all for you! We can't do all the work, you are either getting paid for this, or it's part of your grades and it wouldn't be at all fair for us to do it all for you.
So we need you to do the work, and we will help you when you get stuck. That doesn't mean we will give you a step by step solution you can hand in!
Start by explaining where you are at the moment, and what the next step in the process is. Then tell us what you have tried to get that next step working, and what happened when you did.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Well i cant understand the API, at all and that is why i dont have code to demonstrate. Can you give me an example of a successful purchase and a check if a purchase exist already?
What is this function: GetPurchaseUpdates? Update to what?
What is the Response of the Purchase function? How i can know of a purchase was successful? Where i can find a list will all possible responses?
|
|
|
|
|
If you are trying to handle anything to do with real money - and you are - then you shouldn't be getting code (or anything else) from a "random website"!
Never, ever, accept code from a insecure website to handle anything to do with real money.
You do not know who is giving you the code, you do not know what it does, you do not know that it places the monies correctly into the appropriate account, without passing the details to any third parties.
Only get such code from Amazon themselves - the scope for fraud otherwise is far too large. And remember, you personally could be liable for any monies lost if your action is seen to be negligent - which getting your code from a public forum would most certainly be!
So start looking for API tutorials which explain how to do it, and then check that against the official API documentation. You need to understand this stuff if you are going to use it - or you could well end up coding from a cell ...
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I ask for an example of an API i dont know where this danger that will put you in a cell come from :P
|
|
|
|
|
|
|
This is a followup to a thread started in the Lounge: The Lounge[^]
First this makes me think of The First Law Of Optimization: Don't do it!
And then The Second Law of Optimzation: If you have to do it, don't do it yet!
This obvuiouly refers to peephole optimzation. Selecting an algorithm of lower complexity is a different matter. But worrying about how the switch construct is handled by the compiler falls in the peephole class.
When I programmed Pascal around 1980, the compiler knew a single way of coding CASE x OF statements: As a jump table with at most 256 entries. Or more exactly: Alternatives was limited to internal values 0..255. Fair enough for small student exercises, but for real code, it was just tooo limiting.
Then I got into CHILL with its decision table entries ("CASE A, B, C OF ...", with all combinations of A, B, C values, with "don't care" and ELSE and the whole works - but still static). Later comes C# with much greater flexibility. Bottom line: Tiny jump tables is a very special case. You may hope for the compiler to optimize for that particular one, but I wouldn't spend too much energy on it. In the world of modern code, it is just too special.
Then there is another thing, now that you refer to state machines: I don't believe that switch statements is a good way to implemnent state machines. Maintenance is resource demanding when you expand it with new states and events. Giving proper handling to illegal events is difficult. And any reasonably complete state machine, e.g. for a communication protocol, will have a rather sparse state table.
If you implement a sparse 2D transition table by a jump table for one dimension, each leading to a jump table in the other dimension, you implemenent that full, sparse table in a rather un-maintainable way. I would rather pack the table, and address it as a single table. So you would maintain the table in an unpacked form, and use a small compaction routine to build the runtime table when you change it.
I let the compaction routine generate the table access function as well, so that a simple table not requiring e.g. predicate handling for alternate actions will not have this access code generated.
I guess my code is not super-optimized to those tiny demo-examples to teach students the basic idea of an FSM. But if you really need the functionality of a full blown solution, then I can handle it with sufficient speed that the table lookup is certainly not a bottleneck.
State tables are tables, not "cases". Treat them as such!
|
|
|
|
|
F-ES Sitecore wrote in the Lounge: CBA doing any research, but I don't think .net actually supports the switch statement, it is just syntactic sugar, your switch statement is converted to an "if\else" block by the compiler. Is there any decent compiler around that treats all different variations on switch (/CASE) in exactly the same manner?
Even 40 years ago, when I learned CHILL - from the guy developing the CHILL compiler - did we learn about how the compiler considered various code generating alternatives, from simple jump tables up to if/else sequences as a last resort. The choice was significantly affected by the compiler options selected, whether to optimize for small code space, small data space, fastest execution etc.
This was done 40 years ago. I am sure that compiler developers have not abandoned such options.
|
|
|
|
|
Using my own two eyes, and my personal experience, I have never seen it as anything but an if\else statement. I appreciate there probably are variances, maybe ones I didn't realise were originally switch statements, but from what I've seen the .net compiler seems to heavily favour the if\else.
This convo is a lot like people arguing that god exists....they swear it does, but all I'm asking is for actual simple proof 
|
|
|
|
|
Please help me out am new to see c#
How can I resolve this problem – I use OleDbDataReader to Read the Price from Database to the TextBox “Unit Price” with the currency format but the issues is the Subtotal column calculation Stop working.
See below image 1 for details. As you can see when I read the Price to the Unit Price TextBox as currency format the Subtotal stop working and the total Qty. see the code where I use the currency format.
while (reader.Read())
{
txtPrice.Text = Convert.ToDouble(reader["Price"]).ToString("C",info);
}
<pre>
But if I remove the currency format and use custom format for the textbox "Unit Price" everything work fine but I really want the currency format on the unit Price txtBox.
See when the format is removed and the custom format is used see code below and image 2.
<pre>
while (reader.Read())
{
txtPrice.Text = Convert.ToDouble(reader["Price"]).ToString("#,00.00");
SUBTOTAL CALCULATION CODE
private void Subtolcalculation()
{
for (int i = 0; i < dataGVfrontsale.Rows.Count - 1; i++)
{
CultureInfo info;
info = new CultureInfo("en-NG");
double colPrice = double.Parse(dataGVfrontsale.Rows[i].Cells[4].Value.ToString());
double colQty = double.Parse(dataGVfrontsale.Rows[i].Cells[5].Value.ToString());
double subtol = colPrice * colQty;
dataGVfrontsale.Rows[i].Cells[7].Value = subtol;
}
CODE FOR TOTAL QUANTITY / CURRENCY
private double Total1()
{
double totl = 0;
int i = 0;
for (i = 0; i < dataGVfrontsale.Rows.Count; i++)
{
totl = totl + Convert.ToDouble(dataGVfrontsale.Rows[i].Cells[5].Value);
}
return totl;
}
public void calculColumns()
{
CultureInfo info;
info = new CultureInfo("en-NG");
txtTolQty.Text = Total1().ToString("C", info);
}
<pre>
|
|
|
|
|
Of course it stops working - it fails the Parse operation because you have stuck a currency symbol in front of the number, and it doesn't know what to do with it.
Instead of working with strings all the time put the actual numeric value in the cell, and set the column style to currency - the system will do all the work for you, and there is no conversion needed once the Value is set:
myDataGridView.Columns[columnIWantAsCurrency].DefaultCellStyle.Format = "c";
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
modified 1-Jun-20 3:11am.
|
|
|
|
|
I am attempting to learn programming in c# to begin with and have run into an issue I can't figure out yet. Am hoping fro some help, direction, etc. The code below works just fine when I don't use any multi threading but one I try to multi thread I am getting the following error: "Argument 1: cannot convert from 'void' to 'System.Threading.ThreadStart'" This error is shown on the line "var bkpthread = new Thread(CopyDirectory(source.ToString(), target.ToString()))"
Code:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.Text;
using System.Windows.Forms;
using MessageBox = System.Windows.Forms.MessageBox;
using System.Threading;
class Folders
{
#pragma warning disable IDE1006 // Naming Styles
public string pth { get; set; }
}
namespace Backup_Tool
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
foreach (var Drives in Environment.GetLogicalDrives())
{
System.IO.DriveInfo DriveInf = new System.IO.DriveInfo(Drives);
if (DriveInf.IsReady == true)
{
ddlDrives.Items.Add(DriveInf.Name);
}
}
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) });
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) });
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\" + "Downloads" });
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic) });
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) });
ListView1.Items.Add(new Folders() { pth = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos) });
}
public void ChkDest()
{
string message = "You must first select a destination";
string title = "Destination Error";
MessageBoxButtons buttons = MessageBoxButtons.OK;
MessageBox.Show(message, title, buttons, MessageBoxIcon.Warning);
}
private void Cls_Click(object sender, RoutedEventArgs e)
{
System.Windows.Application.Current.Shutdown();
}
private void AddFolder_Click(object sender, RoutedEventArgs e)
{
var fbd = new FolderBrowserDialog { };
if (fbd.ShowDialog().ToString().Equals("OK"))
ListView1.Items.Add(new Folders() { pth = fbd.SelectedPath });
}
private void FullSystem_Click(object sender, RoutedEventArgs e)
{
ChkDest();
}
private void DelFolder_Click(object sender, RoutedEventArgs e)
{
ListView1.Items.RemoveAt(ListView1.SelectedIndex);
}
private void FldBackup_Click(object sender, RoutedEventArgs e)
{
if (ddlDrives.SelectedIndex > -1 && ddlDrives.SelectedItem != null && !string.IsNullOrEmpty(ddlDrives.SelectedItem.ToString()))
{
foreach (Folders item in ListView1.Items.Cast<folders>())
{
DirectoryInfo source = new DirectoryInfo(item.pth);
string fldname = new DirectoryInfo(item.pth).Name;
DirectoryInfo target = new DirectoryInfo(ddlDrives.SelectedItem + "DataHistory\\" + fldname + "\\");
var bkpthread = new Thread(CopyDirectory(source.ToString(), target.ToString()))
{
IsBackground = true
};
bkpthread.Start();
}
}
else
{
ChkDest();
}
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ddlDrives.SelectedItem.ToString().Contains("C"))
{
FlashTB.Visibility = Visibility.Visible;
tblk1.Visibility = Visibility.Visible;
}
else
{
FlashTB.Visibility = Visibility.Hidden;
tblk1.Visibility = Visibility.Hidden;
}
}
public void CopyDirectory(string SourceDirectory, string TargetDirectory)
{
string ErrorLog = "c:\\temp\\log.txt";
DirectoryInfo source = new DirectoryInfo(SourceDirectory);
DirectoryInfo target = new DirectoryInfo(TargetDirectory);
try
{
if (!source.Exists)
return;
if (!target.Exists)
Directory.CreateDirectory(target.FullName);
}
catch (IOException errorCreate)
{
using (StreamWriter writer = new StreamWriter(ErrorLog))
{
writer.WriteLine(errorCreate.Message);
}
}
try
{
FileInfo[] sourceFiles = source.GetFiles();
for (int i = 0; i < sourceFiles.Length; ++i)
File.Copy(sourceFiles[i].FullName, target.FullName + "\\" + sourceFiles[i].Name, true);
}
catch (IOException copyError)
{
using (StreamWriter writer = new StreamWriter(ErrorLog))
{
writer.WriteLine(copyError.Message);
}
}
try
{
DirectoryInfo[] sourceDirectories = source.GetDirectories();
for (int j = 0; j < sourceDirectories.Length; ++j)
CopyDirectory(sourceDirectories[j].FullName, target.FullName + "\\" + sourceDirectories[j].Name);
}
catch (IOException errorDir)
{
using (StreamWriter writer = new StreamWriter(ErrorLog))
{
writer.WriteLine(errorDir.Message);
}
}
}
}
}
|
|
|
|
|
The function that you give in the Thread constructor takes parameters, therefore ThreadStart is wanting those parameters. Looking at the documentation (HINT HINT HINT), you will see two variants of Thread: one with parameters in the target function and one without parameters. You want parameters, but the documentation says you can only pass ONE parameter. You will need a version of your CopyDirectory that takes one object containing your two parameters -- a class or struct. Something like this (my C# syntax may be off slightly since I'm not compiling this):
class CopyParameters
{
public string source;
public string target;
}
public void CopyDirectoryThreaded (Object obj)
{
string source = ((CopyParameters) obj).source;
string target = ((CopyParameters) obj).target;
CopyDirectory (source, target);
}
and then your calling code looks like
CopyParameters params = new CopyParameters ();
params.source = source.ToString ();
params.target = target.ToString ();
var bkpthread = new Thread(CopyDirectoryThreaded)
{
IsBackground = true
};
bkpthread.Start(params);
Note that you have no way of detecting when your threaded copy is done, nor any protection to prevent two threads from messing with the same directories. You should have both.
Be wary of strong drink. It can make you shoot at tax collectors - and miss.
Lazarus Long, "Time Enough For Love" by Robert A. Heinlein
|
|
|
|
|
Use a delegate in the constructor
var bkpthread = new Thread(delegate() { CopyDirectory(source.ToString(), target.ToString()); })
{
IsBackground = true
};
|
|
|
|
|
Perfect. Thank you so much.
|
|
|
|
|
In my SQL database I have 4 tables with the exact same structure. I want to query them using a generic method.. I've trieed multiple approaches, but I can'te get it right. Here's what I have so far:
private string GetVendorInitials(string tableName, int jobId)
{
using (var db = GetDataContext())
{
var result = string.Empty;
var vendorNames = (from jhv in db.GetTable()
join c in db.Companies on jhv.VendorId equals c.Id
where jhv.JobId == jobId
select c.CompanyName).ToList();
foreach (var vendorName in vendorNames)
{
var vendorInits = new string(vendorName.Split(' ').Select(x => x[0]).ToArray());
result = result + vendorInits + ",";
}
result = result.Trim();
result = result.Substring(0, result.Length - 1);
return result;
}
}
Anyone know how to do this?
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Can you pass the entity type instead of the table name?
You'll also need to have the common entities implement an interface.
public interface IVendorJob
{
int VendorId { get; }
int JobId { get; }
}
...
private string GetVendorInitials<TEntity>(int jobId) where TEntity : IVendorJob
{
using (var db = GetDataContext())
{
var vendorNames = from jhv in db.Set<TEntity>()
join c in db.Companies on jhv.VendorId equals c.Id
where jhv.JobId == jobId
select c.CompanyName;
var vendorInitials = vendorNames.AsEnumerable()
.Select(n => n.Split(' ').Select(x => x[0]).ToArray())
.Select(a => new string(a));
return string.Join(",", vendorInitials);
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi,
I want to read the version information of an exe on FTP. I could not find a resource anywhere. Can someone with knowledge of the subject help me?
|
|
|
|
|
I think you would most likely need to download the file first.
|
|
|
|
|
Thank you for the answer. But I will use the file I want to check as an update file. The file size is up to 150-200Mb. At every update check, downloading the file over and over again poses a problem.
|
|
|
|
|
Then you need to find some other way. FTP allows only certain commands from the remote caller.
|
|
|
|
|
Put the version number in a text file in the same FTP directory, and update it every time you update the exe file.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
FTP is a file transfer protocol: it doesn't care about the content of the files it transfers other than to ensure that the copy is binary identical to the original.
It contains no commands to read a version number from an EXE file as it doesn't know that any file contains a version number, much less where it is.
if you must use FTP, then I'd suggest adding a separate versioning file which contains the version of all files in the folder: download that and you can decide what does and doesn't need updates.
Personally I wouldn't use FTP for updating - you generally need a "more intelligent" solution, although that may use FTP as a transport mechanism. Most update checks are performed by the app checking back to "the mothership", not the other way around - so it already knows what version it is, and communicates that to the remote server for checking.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Thank you for your answer. What kind of update system can I use besides FTP? Is there anything you can offer me?
|
|
|
|
|