MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

Friday, May 30, 2025

Note to self: It's pretty easy to get a Stripe CheckoutSession in .NET.

I talked about writing a Chrome extension and integrating payment through Stripe a few months ago. Unfortunately the "what appears to be an amazingly well-written howto" wasn't, quite.

So here's a quick bit of play with the Stripe API using .NET. This looks up information according to a "Checkout Session" id, which is what you'll get sent if you set up a "After Payment" processor URL, which is what I'm doing -- for now in an Azure Function, just to be kewl.

(No, you shouldn't use hard-coded paths in your code. No, you shouldn't pass around anonymous classed entities in C#. You're right, there's one instance of insanely not-DRY-ness in this code that almost bugs me. And I hate kludging ! after a nullable and would refactor that. This is pretty serious "proof of concept" territory here.)

Steps:

  1. Create a .NET console app.
    • For bonus points, use the newer template without a Main method version, I guess. I got this and was too lazy to start over.
  2. Add stripe.net to your project via your preferred NuGet manager.
  3. Get your Stripe secret key and put it in C:\temp\stripe.key
  4. Paste in the below junk, wiping out the Main method that's in there by default.
  5. Get a CheckoutSession id from, idk, say a Payment Link "After Payment" URL, and insert it into the code.
  6. Profit?
static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");

    StripeConfiguration.ApiKey = File.ReadAllText(@"C:\temp\stripe.key").Trim();
    Service = new Stripe.Checkout.SessionService();

    var csId = "cs_test_a1RRCS1VWdOwOwvpJfdZCtjb9jSVOxp9ZhQibAI1Hv3dsvi6BfCPIWwdZP";
    var info = LookupStripeCheckoutSession(csId);

    Console.WriteLine(JsonConvert.SerializeObject(info, Formatting.Indented));
    File.WriteAllText(
        @"C:\temp\importantStuff.txt",
        JsonConvert.SerializeObject(info, Formatting.Indented)
    );
}

// Stripe Fees:
// https://stripe.com/pricing
// "Starts at 2.9% + 30¢ [per successful transaction for domestic cards]"
// Fudge.
public static object LookupStripeCheckoutSession(string checkoutSessionId)
{
    Session session = Service!.Get(checkoutSessionId);

    if (!session.Metadata.TryGetValue("appId", out var appId))
    {
        throw new MissingFieldException("Checkout session does not contain a product id");
    }

    System.IO.File.WriteAllText(
        @"C:\temp\session.txt",
        JsonConvert.SerializeObject(session, Formatting.Indented)
    );

    return new {
        CheckoutSessionId = session.Id,
        RufworkAppId = appId,
        session.AmountTotal,
        session.Created,

        session.PaymentStatus, // One of: <c>no_payment_required</c>, <c>paid</c>, or <c>unpaid</c>.

        // Note that TotalDetails (as with any child object brought over)
        // has its own json settings, so it'll, eg, serialize to snake case
        // if you don't munge it.
        session.TotalDetails,
        session.CustomerDetails.Email,
    };
}

I'd been thinking about having a $3/year [sic] subscription for an extension. After a test, I learned what the comments, above, mention: That'll cost me ($3 x .029 + 30¢ somehow equals...) 41¢ [???] per transaction.

Okay, apparently there's a "Usage fee" in addition to the charge?

Amount

Fees

Total

Type

Description

Created

Available on

-$0.02

-$0.02

Stripe fee

Billing - Usage Fee (2025-05-26)

May 27

May 27

$3.00

-$0.39

$2.61

Charge

Subscription creation

May 26

Jun 2

From togai.com:

Stripe allows you to perform end-to-end financial transactions with its suite of integrated offerings:

Stripe Billing - This lets you create recurring subscriptions and invoices. Pricing starts at 2.9% + 30¢for every successful transaction.

Usage pricing for pay-as-you-go is 0.7% of billing volume.

If you need a Custom billing domain it is $10 per month.

Stripe Invoicing - Allows you to invoice upto 25 customers for free every month. Thereafter an overage of 0.4% is applied per invoice paid.

Stripe Tax - Offers you tools for tax calculation priced at 0.5% for each transaction.

Stripe Atlas - Assists you with setting up a company at a one-time setup fee of $500.

Stripe Sigma - Offers reporting and analytics using SQL. Pricing starts at 2¢per transaction and includes a $10 fee towards infrastructure. (emphasis mine -mfn)

From bossrevolution.com:

  • Billing. For businesses with a subscription-based or usage-based model, Stripe enables automated billing on a pay-as-you-go or monthly payment basis. Fees start at 0.7% of the billing volume or $620 per month for a one-year contract.

Cool cool. 13 2/3% of $3. Just below Apple's small dev 15%.

Good times. Least the API is simple. More to come.


From stripe.com:

Debit card purchases in the US are more extreme: there are no returns on interchange fees on any US debit card transaction.

If your business often processes refunds shortly after a payment, you can combat these potential lost fees by leaving the transaction authorization open rather than settling the sale right away. This is possible because you only pay interchange fees once a transaction is settled. If you leave the authorization open and a customer makes a return, you can simply reverse the authorization and avoid losing any extra interchange fees (since you never paid those fees to begin with).

For example, if you captured and settled a $100 debit card transaction and a customer requested a return, you could lose $0.42. However, if you had left the authorization open, you could only lose slightly less than $0.04.

You can generally leave an authorization open for up to two days before you pay additional fees, so this approach is most relevant for industries with the immediate delivery of goods (such as food delivery services).

What you can do: Configure the Stripe Payment Intents API to separate authorization and capture.

good heavens.

Labels: , , , ,


posted by Jalindrine at 5/30/2025 01:26:00 PM
Monday, May 26, 2025

Sometimes, I surprise myself:

@Cthutu I wish "It's like the difference of [declaring] an address vs [commanding] directions. The address is useful no matter you are. Whereas directions are invalid if you start somewhere else," was the selected answer all by itself. ;^) 

Labels: ,


posted by Jalindrine at 5/26/2025 03:32:00 PM
Monday, May 19, 2025

I'd missed the whole Forced Reset Trigger debate over the last year or so, but caught an article about it in The Post Sunday and figured I'd see what it's about.

screengrab of washington post article in print edition

Here's what I learned, executive summary style.

  1. The trigger makes things faster for an novice to intermediate shooter once they get the hang of it, but it does require getting the hang of it.
  2. The mechanism seems vaguely similar to a bump stock: Use the energy from the recoil from a shot to help set up the next trigger pull.
  3. For shorter bursts, it's not necessarily that much faster on mag dumps than an experienced shooter with a conventional trigger.
  4. It has very little practical application outside of mag dumps.

My second isn't quite accurate, but check below and it'll make more sense.

First, some YouTube videos and then quotes from an excellent article from the National Association for Gun Rights.

The first shows the trigger in action with some slow-motion demonstrating that a trigger pull is required for each round. But it also is what reminded me of bump stocks. Again, it's not. The bump stock would essentially (afaict, ymmv) push the gun back into your trigger finger so you didn't have to manually pull. It looks like this does something similar but you do have to pull the hairiest trigger possible.

This one shows me two things: The eyes of the first guy shows it fires lots faster than the novice shooter can normally. The second shooter shows me it does take a knack -- easily achieved, but a knack -- to get it to work.

This one, however, is probably my favorite. This fellow shows that he can empty six rounds just as quickly using a traditional trigger. Dude is fast.

Now for some quotes from that article from the National Association for Gun Rights. The last one is pretty telling. They're comparing the Force Reset Trigger to a binary trigger, the second of which also increases discharge rate, but in its case by releasing a round with a pull and a release, thus the whole "binary" moniker. But we learn a decent amount about how actual shooters consider the FRT.

As the BCG [bolt carrier group] moves rearward, it cocks the hammer and simultaneously engages a mechanical component within the trigger assembly designed to push the trigger forward. This forced forward motion ensures a positive reset of the trigger.

...

Forced Reset Triggers significantly increase the rate of fire compared to a standard semi-automatic trigger, similar to the effect of a binary trigger. For shooters who want to send more rounds downrange without the expense and legal hurdles of a full-auto firearm, an FRT can be a viable solution.

...

Ultimately, the FRT is often seen as a fun range accessory rather than a practical tool for precision shooting or tactical applications. While it can provide an exciting experience, shooters must invest time in mastering its operation to use it effectively.

...

An FRT uses the cycling action of the firearm to mechanically force the trigger forward after each shot. This forced reset means your trigger finger moves as the mechanism pushes it forward, rather than through your own conscious effort. While you still feel the movement, the reset action happens automatically due to the interaction between the bolt carrier group and the trigger assembly.

...

If precision shooting is your priority, the binary trigger has a clear advantage. Since the shooter controls when the second round is fired by releasing the trigger, there’s more time to reacquire the target between shots. This control makes the binary trigger better suited for situations where accuracy is more important than the sheer volume of fire.

...

[Which is better in 5 important use cases...]

5. Home Defense:

No Clear Winner — Neither Recommended

Why: In high-stress defensive situations, trigger control and shot accountability are paramount. With a binary trigger, it’s easy to forget about the release shot, which could result in an unintended discharge. The FRT, while mechanically reliable, prioritizes speed over precision, which can make shot placement more difficult when it matters most. For home defense, a standard, high-quality semi-automatic trigger is generally a safer and more practical choice.

That last point (and the "fun range accessory" comment) seem to wrap this up for me. In the wrong hands, a FRT could enable someone shooting at a crowd to cause a lot more carnage than without. For the serious shooter or someone truly interested in eliminating a specific threat, there is no practical usage. That is, unless your adversary was coming at you in waves like an 18-century bayonet charge, it's likely a bad idea to use a FRT for security.

Honestly, I'm still surprised we have a sort of bifurcated Second Amendment. District of Columbia v. Heller is described at today's Wikipedia like this:

The Court also added dicta regarding the private ownership of machine guns. In doing so, it suggested the elevation of the "in common use at the time" prong of the Miller decision, which by itself protects handguns, over the first prong (protecting arms that "have some reasonable relationship to the preservation or efficiency of a well regulated militia"), which may not by itself protect machine guns: "It may be objected that if weapons that are most useful in military service – M16 rifles and the like – may be banned, then the Second Amendment right is completely detached from the prefatory clause. But as we have said, the conception of the militia at the time of the Second Amendment's ratification was the body of all citizens capable of military service, who would bring the sorts of lawful weapons that they possessed at home."

... but there's also otherwise an argument that anything goes. I mean you can even have a fully automatic rifle (as the NAGR references) if you jump through enough hoops. Okay, I realize that's a law, so not 2nd Amendment protection, but in general it feels like "if the Army can own it, so can I" argument comes up more frequently, like with sawed-off shotguns, iirc. That is, in 1939 SCOTUS said:

The Court cannot take judicial notice that a shotgun having a barrel less than 18 inches long has today any reasonable relation to the preservation or efficiency of a well regulated militia, and therefore cannot say that the Second Amendment guarantees to the citizen the right to keep and bear such a weapon.

But I think I saw an argument that since the Army now assigns shotguns in specific urban missions it's fair game for everyone. But even the text, above, seems counter to Heller: The bar there is "Is this weapon useful to a militia?" not Heller's "Is this something a citizen would have at home and bring to a militia?" Anyhow...

And, as I think I mentioned before, years ago I went to a local gun show to see how tough it'd be to get an AR-15 fully auto. There was a bin of the piece that you'd need to replace to make an AR-15 fully auto and a $10 or so book explaining how to do so easily available. For $1500 or so, you could walk out and be an hour or so away from an fully automatic M-16 equivalent.

The question of whether a FRT should be illegal comes down to how you interpret Heller. I don't know that Heller is talking gun technology. That is, a flintlock musket or rifle (two different things) are nothing, not really, like even a lever-action rifle today. If you'd had a lever-action from 2020 or 1970 in 1770, you would have been a power like no other, not just for rate of fire, speed of round (so velocity and inertia -- damage), but also accuracy.

I think Heller is saying more, "What sorts of weapons do you have around the house today?" rather than "Gun technology stops at 1791".

Even then, NAGR would suggest the FRT isn't one of "the sorts of lawful weapons that they possessed at home" when it says "the FRT is often seen as a fun range accessory rather than a practical tool". When it comes to hunting, they add, "When tracking moving game, accurate follow-up shots are often more critical than speed. The binary trigger provides the time and control necessary to line up subsequent shots, whereas the FRT’s rapid reset can make it more difficult to stay on target."

But this does argue for the binary trigger being legal, I believe. (Strangely, binary triggers appear to be illegal in Florida and Alabama, among other states.)

The weird deal the ATF made with the makers of the FRT is a weird middle ground. I assume it's just a punt. From The Post:

Under the terms of a settlement announced Friday by the Justice Department, “forced reset triggers” can be sold legally so long as their manufacturer, Rare Breed Triggers, refrains from developing similar devices for pistols and enforces its patents to stop copycats.

Once the patent or whatever is out, are we back to where we started?

Regardless, this seems to support where I think I always end up on this one: If you don't like our current state, you've got to amend the Amendment.

Labels: ,


posted by ruffin at 5/19/2025 08:41:00 AM
Saturday, May 17, 2025

From Hacker News' Malicious npm Packages Infect 3,200+ Cursor Users With Backdoor, Steal Credentials:

Cybersecurity researchers have flagged three malicious npm packages that are designed to target the Apple macOS version of Cursor, a popular artificial intelligence (AI)-powered source code editor.

"Disguised as developer tools offering 'the cheapest Cursor API,' these packages steal user credentials, fetch an encrypted payload from threat actor-controlled infrastructure, overwrite Cursor's main.js file, and disable auto-updates to maintain persistence," Socket researcher Kirill Boychenko said.

...

"The threat actor's use of the tagline 'the cheapest Cursor API' likely targets this group, luring users with the promise of discounted access while quietly deploying a backdoor," [Socket researcher Kirill Boychenko] added.

None of this is amazing or rocket science, which is exactly why it's important. It's not hard to social engineer humans.

It does make me wonder about the almost unthinking preference devs can have for "[I don't care if it's] NIH". Not that it started with npm. Using brew on macOS or apt-get on Linux are essentially the same thing, but literally anything that auto-updates can be an attack vector.

But if you want to reduce your proverbial footprint, there are ways. Knowing open-source libraries you use well enough to have contributed is one. Not writing an in-house version of something insanely trivial is another.

And, as much as Apple's getting slammed for arguing for its own nanny state right now, using IDEs from fairly reputable sources and not believing deals that are proverbially too good to be true are all probably decent ideas too.

I do wonder about, say, browser extensions. I have one from the EFF on one box that I haven't looked into for a while -- is it still be updated? What do adblock extensions really do? Luckily these are all in JavaScript so, even if obfuscated, you can still sniff through most of them fairly easily. Somebody should know, eventually, if the most popular get wacky. Right?

Though heaven help me if the right vim plugins for IDEs get compromised. I'm toast. (Told ya it was easy.)

Labels: , ,


posted by ruffin at 5/17/2025 02:08:00 PM
Wednesday, May 07, 2025

Coverage reports in C# with Cobertura

In the last contract I completed, we were building WebAPI microservices in C#, and part of our build process was to run all of our XUnit (and, depending on who originally wrote the microservice, maybe NUnit) tests and use SonarCloud to produce a pretty amazingly specific coverage report that highlighted every source file's test status, line-by-line.

I've got a personal project in C#, have been spending time recently writing tests for it, and wondered if I could pull off the same level of coverage testing.

Turns out I can! Exactly the same!

Install by adding a ref to the coverlet collector to your testing proj:

dotnet add package coverlet.collector

Then run the test with this:

dotnet test --collect:"XPlat Code Coverage"

After the above command is run, a coverage.cobertura.xml file containing the results will be published to the TestResults directory as an attachment.

And sure enough, there's a new folder with every run in my C:\path\to\app\MyTestingProjectFolder\TestResults folder. The cobertura file is in painfully named folders like 946f0f00-2eec-4cef-b734-e683d4e258aa, but open the latest and you can see the XML.

Now the XML isn't very interesting.

<coverage line-rate="0.4882" branch-rate="0.465" version="1.9" timestamp="1745620667" lines-covered="1101" lines-valid="2255" branches-covered="299" branches-valid="643">
    <sources>
        <source>C:\path\to\app\MyApp\</source>
    </sources>
    <packages>
        <package name="MyAppPackage" line-rate="0.4882" branch-rate="0.465" complexity="734">
    <classes>
        <class name="Program" filename="Program.cs" line-rate="0" branch-rate="1" complexity="2">
    <methods>
        <method name="<Main>$" signature="(System.String[])" line-rate="0" branch-rate="1" complexity="1">
            <lines>
                <line number="16" hits="0" branch="False"/>
                <line number="20" hits="0" branch="False"/>
            </lines>
    </method>
<!-- ... -->

You have to turn that into a more interesting report.

This Stackoverflow answer explains how:

This tool quickly generate Html reports from coverage file. Works quite well and does not require complex activities, can be easily included in the build process.

From the README:

Install:

dotnet tool install -g dotnet-reportgenerator-globaltool

Run:

reportgenerator [options]

which, in my case, if I'm already in the same folder as the coverage.cobertura.xml file, looks like this:

reportgenerator -reports:".\coverage.cobertura.xml" -targetdir:"coveragereport" -reporttypes:Html

... which, because we said so in the options, creates a folder named coveragereport in the same dir, and within that are two html files that are identical except for their file extensions, index.htm and index.html. Far be it for a Microsoft-adjacent tool to stop producing three-letter file extensions.

And then that report looks incredible:

I have no idea why I redacted so much of that. Also note this is running against a very early stage piece of code. This coverage is obviously horrendous. But the coverage report isn't!

Labels: ,


posted by Jalindrine at 5/07/2025 05:47:00 PM
Tuesday, May 06, 2025

From "Amazon now has a ‘Get book’ button in its iOS Kindle app" on theverge.com:

Apple has appealed the decision, but is also complying with it in the interim, prompting several companies to announce app updates making it easier for users to pay for subscriptions and services. That now includes Amazon’s iOS Kindle app. Contrary to prior limitations, there is now a prominent orange “Get book” button on Kindle app’s book listings.

I just tried. It's there, and, though obviously easy in retrospect, the implementation surprised me:

Kindle iOS kicked me to Safari, which I keep in private mode, and the "Buy with one click" button is activated.

Okay, well shucks. Upon further review, apparently I don't usually pay much attention to that button, because it's always active, even if you're not logged in. Click it and it asks you to sign in.

But I wouldn't expect that to last long. Right now it looks like Amazon is only adding ref_=rekindleDP&nodl=0 to the URL, but they could add a unique, one-use GUID to the link and, with only a little risk to themselves (oh no! we gave away 200k worth of bytes to the wrong person!), make the button "live" immediately.

Adding a one-use, unique "buy now token" would make it easier to buy using Kindle than Apple's own Books. I click buy in Kindle, I click a "buy now!" button in the browser that's opened (no Touch or Face ID required), and a universal link sends me right back to Kindle.

And this would change our relationship with the web on our phones by merging apps and the web in ways I didn't really picture before. Now, Amazon knows where you were when you considered buying a book. They can do an okay job of fingerprinting you, not that they couldn't with you signed in within the app, but the extra data doesn't hurt. And, app review psychosis aside, there's no way for Apple to protect you beyond the way they protect any other surfer on Safari. (And with other default web browsers now on iOS, they can't always even do that.)

This is a pretty significant wall breaking down in the garden. I'm still tempted to think it's a huge positive for devs and users, but watching the platform regain its equilibrium will be fascinating.


That said, I feel like universal links are about to get overhauled.

Apple might soon say...

We'll let you out of iOS, but we're not letting you back in.

Ugh. Tim Apple strikes again.

(Though I haven't heard of them changing the rules there yet. You think Tim Apple's Apple (vs. Phil's Apple) would've had those universal link rule changes in the license already.)


posted by Jalindrine at 5/06/2025 09:46:00 AM

<< Older | Newer >>


MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Support freedom
All posts can be accessed here:


Just the last year o' posts:
  • 05/01/2025 - 06/01/2025
  • 04/01/2025 - 05/01/2025
  • 01/01/2025 - 02/01/2025
  • 12/01/2024 - 01/01/2025
  • 10/01/2024 - 11/01/2024
  • 08/01/2024 - 09/01/2024
  • 07/01/2024 - 08/01/2024
  • 06/01/2024 - 07/01/2024
  • 05/01/2024 - 06/01/2024
  • 04/01/2024 - 05/01/2024
  • 03/01/2024 - 04/01/2024
  • 02/01/2024 - 03/01/2024

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ¢, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.